Start of code to talk to migration daemon to do migrate.
Initial checkin of the migration daemon, xfrd.
40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/python/xen/xend/XendVnet.py
40c9c468x191zetrVlMnExfsQWHxIQ tools/python/xen/xend/__init__.py
40c9c468S2YnCEKmk4ey8XQIST7INg tools/python/xen/xend/encode.py
+40e9808elkoRulOo1GxRTp5ulJGVNw tools/python/xen/xend/packing.py
40c9c468DCpMe542varOolW1Xc68ew tools/python/xen/xend/server/SrvBase.py
40c9c468IxQabrKJSWs0aEjl-27mRQ tools/python/xen/xend/server/SrvConsole.py
40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/python/xen/xend/server/SrvConsoleDir.py
403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format
4050c413NtuyIq5lsYJV4P7KIjujXw tools/xentrace/xentrace_format.1
+40e9808eHO3QprCFKg9l2JJzgt2voA tools/xfrd/Make.xfrd
+40e9808epTR4zWrYjGUnaaynK20Q5A tools/xfrd/Makefile
+40e9808eysqT4VNDlJFqsZB2rdg4Qw tools/xfrd/connection.c
+40e9808eyXfJUi4E0C3WSgrEXqQ1sQ tools/xfrd/connection.h
+40e9808eULGwffNOE4kBrAfZ9YAVMA tools/xfrd/debug.h
+40e9808eyjiahG5uF6AMelNVujBzCg tools/xfrd/enum.c
+40e9808eZpbdn9q2KSSMGCNvY_ZgpQ tools/xfrd/enum.h
+40e9808easXCzzAZQodEfKAhgUXSPA tools/xfrd/hash_table.c
+40e9808e94BNXIVVKBFHC3rnkvwtJg tools/xfrd/hash_table.h
+40e9808epW9iHcLXuO3QfUfLzB7onw tools/xfrd/lexis.c
+40e9808egccMhCizayQRGtpBA3L5MQ tools/xfrd/lexis.h
+40e9808ePADCSKL1YgGCt2TbYPnYkw tools/xfrd/lzi_stream.c
+40e9808eDNAdpF71o5teYb9DTT-PRw tools/xfrd/lzi_stream.h
+40e9808eQxi0EzTcPJtosrzxEIjA-Q tools/xfrd/marshal.c
+40e9808etg13xfRm0Lqd8vY-jHOoTg tools/xfrd/marshal.h
+40e9808eCsmywryb036TdtRMJHDMmQ tools/xfrd/select.c
+40e9808e99OcM547cKMTfmCVSoWVAw tools/xfrd/select.h
+40e9808e5_PLdodqVOSx0b4T_f5aeg tools/xfrd/sxpr.c
+40e9808e0O4sHZtkDv5hlSqjYcdQAQ tools/xfrd/sxpr.h
+40e9808eF3NVldqRNS5IHM8gbFAvpw tools/xfrd/xdr.c
+40e9808ezXzoRHm7pybXU69NtnjimA tools/xfrd/xdr.h
+40e9808edpUtf4bJ8IbqClPJj_OvbA tools/xfrd/xen_domain.c
+40e9808eHviFFIwdUKOA234uIeifjA tools/xfrd/xen_domain.h
+40e9808eIFeV-MDCNyVTNt5NfMPKeQ tools/xfrd/xfrd.c
+40e9808eGIbOoSNJRiwWK2C3mjGWaA tools/xfrd/xfrd.h
+40e9808eHXvs_5eggj9McD_J90mhNw tools/xfrd/xfrdClient.py
3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING
3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile
3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk
#include "string_stream.h"
#include "allocate.h"
-static int string_print(IOStream *io, const char *msg, va_list args);
-static int string_getc(IOStream *io);
static int string_error(IOStream *io);
static int string_close(IOStream *io);
static void string_free(IOStream *io);
return (StringData*)io->data;
}
-/** Read a character from a string stream.
- *
- * @param io string stream
- * @return character read, IOSTREAM_EOF if no more input
- */
-static int string_getc(IOStream *io){
- StringData *data = get_string_data(io);
- int c = IOSTREAM_EOF;
- char *s = data->in;
-
- if(s && s < data->end){
- c = (unsigned)*s;
- data->in = s+1;
- }
- return c;
-}
-
-/** Print to a string stream.
- * Formats the data to an internal buffer and prints it.
- * The formatted data must fit into the internal buffer.
- *
- * @param io string stream
- * @param format print format
- * @param args print arguments
- * @return result of the print
- */
-static int string_print(IOStream *io, const char *msg, va_list args){
- StringData *data = get_string_data(io);
- int k = data->end - data->out;
- int n = vsnprintf(data->out, k, (char*)msg, args);
- if(n < 0 || n > k ){
- n = k;
- IOStream_close(io);
- } else {
- data->out += n;
- }
- return n;
-}
-
/** Test if a string stream has an error.
*
* @param io string stream
*/
static void string_free(IOStream *io){
StringData *data = get_string_data(io);
- zero(data, sizeof(*data));
+ memzero(data, sizeof(*data));
deallocate(data);
}
*/
void string_stream_init(IOStream *io, StringData *data, char *s, int n){
if(data && io){
- zero(data, sizeof(*data));
+ memzero(data, sizeof(*data));
data->string = (char*)s;
data->in = data->string;
data->out = data->string;
data->size = n;
data->end = data->string + n;
- zero(io, sizeof(*io));
+ memzero(io, sizeof(*io));
io->methods = &string_methods;
io->data = data;
}
import sys
import socket
+import time
+
+from twisted.internet import reactor
+from twisted.internet import defer
+from twisted.internet.protocol import Protocol
+from twisted.internet.protocol import ClientFactory
import sxp
import XendDB
import EventServer; eserver = EventServer.instance()
+from xen.xend.packing import SxpPacker, SxpUnpacker
+from xen.xend import XendDomain
+xd = XendDomain.instance()
+
+
+XFRD_PORT = 8002
+
+XFR_PROTO_MAJOR = 1
+XFR_PROTO_MINOR = 0
+
+class Migrate(Protocol):
+
+ def __init__(self, minfo):
+ self.packer = None
+ self.unpacker = None
+ self.minfo = minfo
+
+ def connectionMade(self):
+ self.packer = SxpPacker(self.transport)
+ self.unpacker = SxpPacker()
+ # Send hello.
+ self.packer.pack(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR])
+ # Send migrate.
+ vmconfig = self.minfo.vmconfig()
+ if not vmconfig:
+ self.loseConnection()
+ return
+ self.packer.pack(['xfr.migrate',
+ self.minfo.src_dom,
+ vmconfig,
+ self.minfo.dst_host,
+ self.minfo.dst_port])
+
+ def connectionLost(self, reason):
+ self.minfo.closed(reason)
+
+ def dataReceived(self, data):
+ try:
+ self.unpacker.reset(data)
+ val = self.unpacker.unpack()
+ print 'dataReceived>', 'val=', val
+ op = val[0]
+ op.replace('.', '_')
+ if op.startwith('xfr_'):
+ fn = getattr(self, op, self.unknown)
+ else:
+ fn = self.unknown
+ fn(val)
+ except Exception, ex:
+ print 'dataReceived>', ex
+ pass
+
+ def unknown(self, val):
+ print 'unknown>', val
+
+ def xfr_progress(self, val):
+ print 'xfr_progress>', val
+
+ def xfr_error(self, val):
+ # If we get an error with non-zero code the migrate failed.
+ # An error with code zero indicates hello success.
+ err = int(val[1])
+ if not err: return
+ self.minfo.error(err);
+ self.loseConnection()
+
+ def xfr_ok(self, val):
+ # An ok indicates migrate completed successfully, and contains
+ # the new domain id on the remote system.
+ dom = int(val[1])
+ self.minfo.ok(dom)
+ self.loseConnection()
+
+class MigrateClientFactory(ClientFactory):
+
+ def __init__(self, minfo):
+ ClientFactory.__init__(self)
+ self.minfo = minfo
+
+ def startedConnecting(self, connector):
+ print 'Started to connect', 'self=', self, 'connector=', connector
+
+ def buildProtocol(self, addr):
+ print 'buildProtocol>', addr
+ return Migrate(self.minfo)
+
+ def clientConnectionLost(self, connector, reason):
+ print 'clientConnectionLost>', 'connector=', connector, 'reason=', reason
+
+ def clientConnectionFailed(self, connector, reason):
+ print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason
+
+
class XendMigrateInfo:
# states: begin, active, failed, succeeded?
- def __init__(self, id, dom, dst):
+ def __init__(self, id, dom, host, port):
self.id = id
self.state = 'begin'
self.src_host = socket.gethostname()
self.src_dom = dom
self.dst_host = dst
+ self.dst_port = port
self.dst_dom = None
+ self.start = 0
+ self.deferred = defer.Deferred()
def set_state(self, state):
self.state = state
sxpr = ['migrate', ['id', self.id], ['state', self.state] ]
sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
sxpr.append(sxpr_src)
- sxpr_dst = ['dst', ['host', self.dst] ]
+ sxpr_dst = ['dst', ['host', self.dst_host] ]
if self.dst_dom:
sxpr_dst.append(['domain', self.dst_dom])
sxpr.append(sxpr_dst)
return sxpr
-
+
+ def vmconfig(self):
+ dominfo = xd.domain_get(self.dom)
+ if dominfo:
+ val = return sxp.to_string(dominfo)
+ else:
+ val = None
+ return None
+
+ def error(self, err):
+ self.state = 'error'
+
+ def ok(self, dom):
+ self.state = 'ok'
+ self.dst_dom = dom
+
+ def close(self):
+ if self.state =='ok':
+ eserver.inject('xend.migrate.ok', self.sxpr())
+ else:
+ self.state = 'error'
+ eserver.inject('xend.migrate.error', self.sxpr())
class XendMigrate:
# Represents migration in progress.
def migrate_get(self, id):
return self.migrate.get(id)
- def migrate_begin(self, dom, dst):
+ def migrate_begin(self, dom, host):
# Check dom for existence, not migrating already.
- # Create migrate info, tell xend to migrate it?
- # - or fork migrate command ourselves?
# Subscribe to migrate notifications (for updating).
id = self.nextid()
- info = XenMigrateInfo(id, dom, dst)
+ info = XenMigrateInfo(id, dom, host, XFRD_PORT)
self._add_migrate(id, info)
- return id
+ mcf = MigrateClientFactory(info)
+ reactor.connectTCP('localhost', XFRD_PORT, mcf)
+ return info.deferred
def instance():
global inst
--- /dev/null
+
+# XDR-style packer/unpacker for sxpr.
+#
+# string -> [STRING] [len:u16] <len bytes>
+# atom -> [ATOM] [len:u16] <len bytes>
+# int -> [UINT] [value]
+# list -> [LIST] {1 elt}* 0
+# null -> [NULL]
+# none -> [NONE]
+# bool -> [BOOL] { 0:u8 | 1:u8 }
+#
+# types packed as u16.
+#
+# So (a b c) -> [LIST] 1 a 1 b 1 c 0
+# () -> [LIST] 0
+
+import struct
+
+try:
+ from cStringIO import StringIO as _StringIO
+except ImportError:
+ from StringIO import StringIO as _StringIO
+
+import types
+
+class Error(Exception):
+
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __repr__(self):
+ return repr(self.msg)
+
+ def __str__(self):
+ return str(self.msg)
+
+
+class ConversionError(Error):
+ pass
+
+BOOL_SIZE = 1
+BOOL_FMT = '>B'
+
+BYTE_SIZE = 1
+BYTE_FMT = '>b'
+UBYTE_FMT = '>B'
+
+SHORT_SIZE = 2
+SHORT_FMT = '>h'
+USHORT_FMT = '>H'
+
+INT_SIZE = 4
+INT_FMT = '>l'
+UINT_FMT = '>L'
+
+NONE_CODE = 0
+NULL_CODE = 1
+INT_CODE = 2
+STRING_CODE = 3
+ATOM_CODE = 4
+BOOL_CODE = 5
+LIST_CODE = 10
+
+class Packer:
+
+ def __init__(self, io=None):
+ self.reset(io=io)
+
+ def reset(self, io=None):
+ if io is None:
+ io = _StringIO()
+ self.io = io
+
+ def get_buffer(self):
+ return self.io.getvalue()
+
+ def get_io(self):
+ return self.io
+
+ def struct_pack(self, fmt, x):
+ try:
+ self.io.write(struct.pack(fmt, x))
+ except struct.error, msg:
+ raise ConversionError, msg
+
+ def pack_none(self):
+ pass
+
+ def pack_bool(self, x):
+ # { '1' | '0' }
+ print 'bool>', x
+ if x:
+ self.io.write('\1')
+ else:
+ self.io.write('\0')
+
+ def pack_byte(self, x):
+ self.struct_pack(BYTE_FMT, x & 0xff)
+
+ def pack_char(self, x):
+ print 'char>', x
+ self.io.write(x)
+
+ def pack_ubyte(self, x):
+ print 'ubyte>', x
+ self.struct_pack(UBYTE_FMT, x & 0xff)
+
+ def pack_ushort(self, x):
+ print 'ushort>', x
+ self.struct_pack(USHORT_FMT, x & 0xffff)
+
+ def pack_short(self, x):
+ print 'short>', x
+ self.struct_pack(SHORT_FMT, x & 0xffff)
+
+ def pack_uint(self, x):
+ print 'uint>', x
+ self.struct_pack(UINT_FMT, x)
+
+ def pack_int(self, x):
+ print 'int>', x
+ self.struct_pack(INT_FMT, x)
+
+ def pack_uhyper(self, x):
+ print 'uhyper>', x
+ self.pack_uint(x>>32 & 0xffffffffL)
+ self.pack_uint(x & 0xffffffffL)
+
+ pack_hyper = pack_uhyper
+
+ def pack_fstring(self, n, x):
+ print 'fstring>', x
+ self.io.write(x)
+
+ pack_fopaque = pack_fstring
+
+ def pack_string(self, x):
+ print 'string>', x
+ n = len(x)
+ self.pack_ushort(n)
+ self.pack_fstring(n, x)
+
+ pack_opaque = pack_string
+ pack_bytes = pack_string
+
+ def pack_list(self, x, pack_item):
+ print 'list>', x
+ # { '1' <item> }* '0'
+ for item in x:
+ self.pack_bool(1)
+ pack_item(item)
+ self.pack_bool(0)
+
+ def pack_farray(self, x, pack_item):
+ # <item>*
+ # Can pass n and check length - but is it worth it?
+ print 'farray>', list
+ for item in x:
+ pack_item(item)
+
+ def pack_array(self, x, pack_item):
+ # n <item>*n
+ print 'array>', x
+ self.pack_uint(len(x))
+ self.pack_farray(x, pack_item)
+
+class Unpacker:
+
+ def __init__(self, data):
+ self.reset(data)
+
+ def reset(self, data):
+ if isinstance(data, types.StringType):
+ data = _StringIO(data)
+ self.io = data
+
+ def get_bytes(self, n):
+ if n < 0:
+ raise ConversionError('negative byte count')
+ data = self.io.read(n)
+ return data
+
+ def struct_unpack(self, fmt, n):
+ data = self.get_bytes(n)
+ try:
+ return struct.unpack(fmt, data)[0]
+ except struct.error, msg:
+ raise ConversionError, msg
+
+ def unpack_none(self):
+ return None
+
+ def unpack_bool(self):
+ return self.struct_unpack(BOOL_FMT, BOOL_SIZE)
+
+ def unpack_char(self):
+ return self.get_bytes(1)[0]
+
+ def unpack_byte(self):
+ return self.struct_unpack(BYTE_FMT, BYTE_SIZE)
+
+ def unpack_ubyte(self):
+ return self.struct_unpack(UBYTE_FMT, BYTE_SIZE)
+
+ def unpack_ushort(self):
+ return self.struct_unpack(USHORT_FMT, SHORT_SIZE)
+
+ def unpack_short(self):
+ return self.struct_unpack(SHORT_FMT, SHORT_SIZE)
+
+ def unpack_uint(self):
+ x = self.struct_unpack(UINT_FMT, UINT_SIZE)
+ try:
+ return int(x)
+ except OverflowError:
+ return x
+
+ def unpack_int(self):
+ return self.struct_unpack(INT_FMT, INT_SIZE)
+
+ def unpack_uhyper(self):
+ hi = self.unpack_uint()
+ lo = self.unpack_uint()
+ return long(hi)<<32 | lo
+
+ def unpack_hyper(self):
+ x = self.unpack_uhyper()
+ if x >= 0x8000000000000000L:
+ x = x - 0x10000000000000000L
+ return x
+
+ def unpack_fstring(self, n):
+ return self.get_bytes(n)
+
+ unpack_fopaque = unpack_fstring
+
+ def unpack_string(self):
+ n = self.unpack_ushort()
+ return self.unpack_fstring(n)
+
+ unpack_opaque = unpack_string
+ unpack_bytes = unpack_string
+
+ def unpack_list(self, unpack_item):
+ list = []
+ while self.unpack_bool():
+ list.append(unpack_item())
+ return list
+
+ def unpack_farray(self, n, unpack_item):
+ list = []
+ for i in range(n):
+ list.append(unpack_item())
+ return list
+
+ def unpack_array(self, unpack_item):
+ n = self.unpack_ushort()
+ return self.unpack_farray(n, unpack_item)
+
+class SxpPacker(Packer):
+
+ pack_code = Packer.pack_ushort
+
+ def pack(self, x):
+ if isinstance(x, types.NoneType):
+ self.pack_code(NONE_CODE)
+ self.pack_none()
+ elif isinstance(x, types.IntType):
+ self.pack_code(INT_CODE)
+ self.pack_int(x)
+ elif isinstance(x, types.StringType):
+ self.pack_code(STRING_CODE)
+ self.pack_string(x)
+ elif isinstance(x, types.ListType):
+ self.pack_code(LIST_CODE)
+ self.pack_list(x, self.pack)
+ else:
+ raise Error('invalid type ' + str(type(x)))
+
+class SxpUnpacker(Unpacker):
+
+ unpack_code = Unpacker.unpack_ushort
+
+ def unpack(self):
+ code = self.unpack_code()
+ if code == NONE_CODE:
+ val = self.unpack_none()
+ elif code == INT_CODE:
+ val = self.unpack_int()
+ elif code == BOOL_CODE:
+ val = self.unpack_bool()
+ elif code == STRING_CODE:
+ val = self.unpack_string()
+ elif code == ATOM_CODE:
+ val = self.unpack_string()
+ elif code == LIST_CODE:
+ val = self.unpack_list(self.unpack)
+ else:
+ raise Error('invalid code ' + str(code))
+ return val
+
+def main():
+ d = "['vfarm', ['@', ['name', 'vfarm1']], ['memory', 1024], ['image', 'splinux'], ['args', 'root=/dev/nfs ip=dhcp'], [ 1, -1, 1000000]]"
+ print"> len=", len(d), "d=", d
+ obj = ['vfarm', ['@', ['name', 'vfarm1']],
+ ['memory', 1024],
+ ['image', 'splinux'],
+ ['args', 'root=/dev/nfs ip=dhcp'],
+ [ 1, -1, 1000000] ]
+ print "> obj=", obj
+ pack = SxpPacker()
+ pack.pack(obj)
+ data = pack.get_buffer()
+ print "> len=", len(data), "data=", data
+ unpack = SxpUnpacker(data)
+ obj_unpack = unpack.unpack()
+ print "> obj=", obj_unpack
+ #obj = [100,101,102, 999.00234, { 'a': 1, 'b': 2 } ]
+ #pack.reset()
+ #pack.pack_item(obj)
+ #data = pack.get_buffer()
+ #print "> obj=", obj
+ #print "> len=", len(data), "data=", data
+ #unpack.reset(data)
+ #obj_unpack = unpack.unpack_item()
+ #print "> obj=", obj_unpack
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+# -*- mode: Makefile; -*-
+#============================================================================
+
+UTIL_LIB = libutil.a
+
+UTIL_LIB_SRC =
+UTIL_LIB_SRC += allocate.c
+UTIL_LIB_SRC += enum.c
+UTIL_LIB_SRC += file_stream.c
+UTIL_LIB_SRC += gzip_stream.c
+UTIL_LIB_SRC += hash_table.c
+UTIL_LIB_SRC += iostream.c
+UTIL_LIB_SRC += lexis.c
+UTIL_LIB_SRC += lzi_stream.c
+UTIL_LIB_SRC += marshal.c
+UTIL_LIB_SRC += string_stream.c
+UTIL_LIB_SRC += sxpr.c
+#UTIL_LIB_SRC += sxpr_parser.c
+UTIL_LIB_SRC += sys_net.c
+UTIL_LIB_SRC += sys_string.c
+#UTIL_LIB_SRC += util.c
+UTIL_LIB_SRC += xdr.c
+
+#----------------------------------------------------------------------------
+# Xfrd.
+
+XFRD_PROG_SRC =
+XFRD_PROG_SRC += xfrd.c
+#XFRD_PROG_SRC += xfr_msg.c
+XFRD_PROG_SRC += xen_domain.c
+XFRD_PROG_SRC += select.c
+XFRD_PROG_SRC += connection.c
+
+#============================================================================
--- /dev/null
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Mike Wray <mike.wray@hp.com>
+#============================================================================
+
+XEN_ROOT = ../..
+include $(XEN_ROOT)/tools/Make.defs
+
+XFRD_INSTALL_DIR = /usr/sbin
+
+vpath %.h $(XEN_HYPERVISOR_IFS)
+INCLUDES += -I $(XEN_HYPERVISOR_IFS)
+
+vpath %h $(XEN_LINUX_INCLUDE)
+INCLUDES += -I $(XEN_LINUX_INCLUDE)
+
+vpath %.h $(XEN_XU)
+INCLUDES += -I $(XEN_XU)
+
+vpath %c $(XEN_LIBXUTIL)
+INCLUDES += -I $(XEN_LIBXUTIL)
+
+include Make.xfrd
+
+UTIL_LIB_OBJ = $(UTIL_LIB_SRC:.c=.o)
+
+XFRD_PROG_OBJ = $(XFRD_PROG_SRC:.c=.o)
+XFRD_PROG_OBJ += $(UTIL_LIB)
+
+CPPFLAGS += -D _XEN_XFR_STUB_
+
+CFLAGS += -g
+CFLAGS += -Wall
+CFALGS += -Werror
+CFLAGS += $(INCLUDES)
+# Make gcc generate dependencies.
+CFLAGS += -Wp,-MD,.$(@F).d
+PROG_DEP = .*.d
+
+#LDFLAGS += -L $(COMPRESS_DIR) -lz
+
+$(warning XFRD_PROG_OBJ= $(XFRD_PROG_OBJ))
+$(warning UTIL_LIB= $(UTIL_LIB))
+$(warning UTIL_LIB_OBJ= $(UTIL_LIB_OBJ))
+
+all: xfrd
+
+xfrd: $(XFRD_PROG_OBJ) -lz
+
+.PHONY: install
+install: xfrd
+ mkdir -p $(prefix)/$(XFRD_INSTALL_DIR)
+ install -m 0755 xfrd $(prefix)/$(XFRD_INSTALL_DIR)
+
+.PHONY: libutil
+libutil: $(UTIL_LIB)
+
+$(UTIL_LIB): $(UTIL_LIB_OBJ)
+ $(AR) rc $@ $^
+
+.PHONY: clean
+clean:
+ $(RM) *.o *.a *.so *~ xfrd
+ $(RM) $(PROG_DEP)
+
+-include $(PROG_DEP)
+
--- /dev/null
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "connection.h"
+#include "file_stream.h"
+#include "lzi_stream.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN] %s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO] %s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] %s" fmt, __FUNCTION__, ##args)
+
+/** Compress magic header. */
+char compress_magic[2] = { 0x1f, 0x8b };
+
+/** Plain magic header. */
+char plain_magic[2] = { 0x0, 0x0 };
+
+int Conn_read_header(int sock, int *flags){
+ int err = 0;
+ char magic[2] = {};
+ int k, n = sizeof(magic);
+ k = read(sock, magic, n);
+ if(k != n){
+ err = -EINVAL;
+ goto exit;
+ }
+ dprintf("> magic={ 0x%x, 0x%x }\n", magic[0], magic[1]);
+ if(magic[0] == compress_magic[0] && magic[1] == compress_magic[1]){
+ *flags |= CONN_READ_COMPRESS;
+ dprintf("> Using compress read.\n");
+ } else {
+ dprintf("> Using plain read.\n");
+ }
+ exit:
+ return err;
+}
+
+int Conn_write_header(int sock, int flags){
+ int err = 0;
+ if(flags & CONN_WRITE_COMPRESS){
+ dprintf("> Using compress write.\n");
+ err = write(sock, compress_magic, 2);
+ } else {
+ dprintf("> Using plain write.\n");
+ err = write(sock, plain_magic, 2);
+ }
+ if(err == 2) err = 0;
+ return err;
+}
+
+/** Initialize a file stream from a file desciptor.
+ *
+ * @param fd file descriptor
+ * @param mode file mode
+ * @param flags control compression and buffering
+ * @param io return parameter for the stream
+ * @return 0 on success, error code otherwise
+ */
+int stream_init(int fd, const char *mode, int flags, int compress, IOStream **io){
+ int err = 0;
+ dprintf(">mode=%s flags=%x compress=%d\n", mode, flags, compress);
+ if(compress){
+ *io = lzi_stream_fdopen(fd, mode);
+ } else {
+ *io = file_stream_fdopen(fd, mode);
+ }
+ if(!*io){
+ err = -errno;
+ perror("fdopen");
+ goto exit;
+ }
+ if(1 && (flags & CONN_NOBUFFER)){
+ // Make unbuffered.
+ dprintf("> unbuffer...\n");
+ err = file_stream_setvbuf((compress ? lzi_stream_io(*io) : *io), NULL, _IONBF, 0);
+ if(err){
+ err = -errno;
+ perror("setvbuf");
+ goto exit;
+ }
+ }
+ exit:
+ if(err && *io){
+ dprintf("> close err=%d\n", err);
+ IOStream_close(*io);
+ *io = NULL;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize a connection.
+ *
+ * @param conn connection
+ * @param flags
+ * @param sock socket
+ * @param ipaddr ip address
+ * @return 0 on success, error code otherwise
+ */
+int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr){
+ int err = 0;
+ dprintf("> flags=%x\n", flags);
+ conn->addr = addr;
+ conn->sock = sock;
+ dprintf("> write stream...\n");
+ err = stream_init(sock, "w", flags, (flags & CONN_WRITE_COMPRESS), &conn->out);
+ if(err) goto exit;
+ IOStream_flush(conn->out);
+ dprintf("> read stream...\n");
+ err = stream_init(sock, "r", flags, (flags & CONN_READ_COMPRESS) , &conn->in);
+ if(err) goto exit;
+ exit:
+ if(err) eprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Open a connection.
+ *
+ * @param conn connection
+ * @param flags
+ * @param ipaddr ip address to connect to
+ * @param port port
+ * @return 0 on success, error code otherwise
+ */
+int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port){
+ int err = 0;
+ int sock;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ socklen_t addr_n = sizeof(addr_in);
+ dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port));
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if(sock < 0){
+ err = -errno;
+ goto exit;
+ }
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr = ipaddr;
+ addr_in.sin_port = port;
+ err = connect(sock, addr, addr_n);
+ if(err) goto exit;
+ //err = Conn_write_header(sock, flags);
+ //if(err < 0) goto exit;
+ err = Conn_init(conn, flags, sock, addr_in);
+ exit:
+ if(err) eprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Close a connection.
+ *
+ * @param conn connection
+ */
+void Conn_close(Conn *conn){
+ if(conn->in) IOStream_close(conn->in);
+ if(conn->out) IOStream_close(conn->out);
+ shutdown(conn->sock, 2);
+}
--- /dev/null
+/* $Id: connection.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */
+#ifndef _VFC_CONNECTION_H_
+#define _VFC_CONNECTION_H_
+
+#include <netinet/in.h>
+
+#include "iostream.h"
+
+/** A connection.
+ * The underlying transport is a socket.
+ * Contains in and out streams using the socket.
+ */
+typedef struct Conn {
+ struct sockaddr_in addr;
+ int sock;
+ IOStream *in;
+ IOStream *out;
+} Conn;
+
+enum {
+ CONN_NOBUFFER=1,
+ CONN_READ_COMPRESS=2,
+ CONN_WRITE_COMPRESS=4,
+};
+
+extern int Conn_read_header(int sock, int *flags);
+extern int Conn_write_header(int sock, int flags);
+extern int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr);
+extern int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port);
+extern void Conn_close(Conn *conn);
+
+#endif /* ! _VFC_CONNECTION_H_ */
--- /dev/null
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _XUTIL_DEBUG_H_
+#define _XUTIL_DEBUG_H_
+
+#ifndef MODULE_NAME
+#define MODULE_NAME ""
+#endif
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) printk(KERN_DEBUG "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#else
+
+#include <stdio.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#endif
+
+/** Print format for an IP address.
+ * See NIPQUAD(), HIPQUAD()
+ */
+#define IPFMT "%u.%u.%u.%u"
+
+#endif /* ! _XUTIL_DEBUG_H_ */
--- /dev/null
+/*
+ * Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#else
+#include <errno.h>
+#endif
+
+#include "sys_string.h"
+#include "enum.h"
+
+/** Map an enum name to its value using a table.
+ *
+ * @param name enum name
+ * @param defs enum definitions
+ * @return enum value or -1 if not known
+ */
+int enum_name_to_val(char *name, EnumDef *defs){
+ int val = -1;
+ for(; defs->name; defs++){
+ if(!strcmp(defs->name, name)){
+ val = defs->val;
+ break;
+ }
+ }
+ return val;
+}
+
+/** Map an enum value to its name using a table.
+ *
+ * @param val enum value
+ * @param defs enum definitions
+ * @param defs_n number of definitions
+ * @return enum name or NULL if not known
+ */
+char *enum_val_to_name(int val, EnumDef *defs){
+ char *name = NULL;
+ for(; defs->name; defs++){
+ if(val == defs->val){
+ name = defs->name;
+ break;
+ }
+ }
+ return name;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XUTIL_ENUM_H_
+#define _XUTIL_ENUM_H_
+
+/** Mapping of an enum value to a name. */
+typedef struct EnumDef {
+ int val;
+ char *name;
+} EnumDef;
+
+extern int enum_name_to_val(char *name, EnumDef *defs);
+extern char *enum_val_to_name(int val, EnumDef *defs);
+
+#endif /* _XUTIL_ENUM_H_ */
--- /dev/null
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef __KERNEL__
+# include <linux/config.h>
+# include <linux/module.h>
+# include <linux/kernel.h>
+# include <linux/errno.h>
+#else
+# include <errno.h>
+# include <stddef.h>
+#endif
+
+//#include <limits.h>
+
+#include "allocate.h"
+#include "hash_table.h"
+
+/** @file
+ * Base support for hashtables.
+ *
+ * Hash codes are reduced modulo the number of buckets to index tables,
+ * so there is no need for hash functions to limit the range of hashcodes.
+ * In fact it is assumed that hashcodes do not change when the number of
+ * buckets in the table changes.
+ */
+
+/*==========================================================================*/
+/** Number of bits in half a word. */
+//#if __WORDSIZE == 64
+//#define HALF_WORD_BITS 32
+//#else
+#define HALF_WORD_BITS 16
+//#endif
+
+/** Mask for lo half of a word. On 32-bit this is
+ * (1<<16) - 1 = 65535 = 0xffff
+ * It's 4294967295 = 0xffffffff on 64-bit.
+ */
+#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1)
+
+/** Get the lo half of a word. */
+#define LO_HALF(x) ((x) & LO_HALF_MASK)
+
+/** Get the hi half of a word. */
+#define HI_HALF(x) ((x) >> HALF_WORD_BITS)
+
+/** Do a full hash on both inputs, using DES-style non-linear scrambling.
+ * Both inputs are replaced with the results of the hash.
+ *
+ * @param pleft input/output word
+ * @param pright input/output word
+ */
+void pseudo_des(unsigned long *pleft, unsigned long *pright){
+ // Bit-rich mixing constant.
+ static const unsigned long a_mixer[] = {
+ 0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, };
+
+ // Bit-rich mixing constant.
+ static const unsigned long b_mixer[] = {
+ 0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, };
+
+ // Number of iterations - must be 2 or 4.
+ static const int ncycle = 4;
+ //static const int ncycle = 2;
+
+ unsigned long left = *pleft, right = *pright;
+ unsigned long v, v_hi, v_lo;
+ int i;
+
+ for(i=0; i<ncycle; i++){
+ // Flip some bits in right to get v.
+ v = right;
+ v ^= a_mixer[i];
+ // Get lo and hi halves of v.
+ v_lo = LO_HALF(v);
+ v_hi = HI_HALF(v);
+ // Non-linear mix of the halves of v.
+ v = ((v_lo * v_lo) + ~(v_hi * v_hi));
+ // Swap the halves of v.
+ v = (HI_HALF(v) | (LO_HALF(v) << HALF_WORD_BITS));
+ // Flip some bits.
+ v ^= b_mixer[i];
+ // More non-linear mixing.
+ v += (v_lo * v_hi);
+ v ^= left;
+ left = right;
+ right = v;
+ }
+ *pleft = left;
+ *pright = right;
+}
+
+/** Hash a string.
+ *
+ * @param s input to hash
+ * @return hashcode
+ */
+Hashcode hash_string(char *s){
+ Hashcode h = 0;
+ if(s){
+ for( ; *s; s++){
+ h = hash_2ul(h, *s);
+ }
+ }
+ return h;
+}
+
+/** Get the bucket for a hashcode in a hash table.
+ *
+ * @param table to get bucket from
+ * @param hashcode to get bucket for
+ * @return bucket
+ */
+inline HTBucket * get_bucket(HashTable *table, Hashcode hashcode){
+ return table->buckets + (hashcode % table->buckets_n);
+}
+
+/** Initialize a hash table.
+ * Can be safely called more than once.
+ *
+ * @param table to initialize
+ */
+void HashTable_init(HashTable *table){
+ int i;
+
+ if(!table->init_done){
+ table->init_done = 1;
+ table->next_id = 0;
+ for(i=0; i<table->buckets_n; i++){
+ HTBucket *bucket = get_bucket(table, i);
+ bucket->head = 0;
+ bucket->count = 0;
+ }
+ table->entry_count = 0;
+ }
+}
+
+/** Allocate a new hashtable.
+ * If the number of buckets is not positive the default is used.
+ * The number of buckets should usually be prime.
+ *
+ * @param buckets_n number of buckets
+ * @return new hashtable or null
+ */
+HashTable *HashTable_new(int buckets_n){
+ HashTable *z = ALLOCATE(HashTable);
+ if(!z) goto exit;
+ if(buckets_n <= 0){
+ buckets_n = HT_BUCKETS_N;
+ }
+ z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
+ if(!z->buckets){
+ deallocate(z);
+ z = 0;
+ goto exit;
+ }
+ z->buckets_n = buckets_n;
+ HashTable_init(z);
+ exit:
+ return z;
+}
+
+/** Free a hashtable.
+ * Any entries are removed and freed.
+ *
+ * @param h hashtable (ignored if null)
+ */
+void HashTable_free(HashTable *h){
+ if(h){
+ HashTable_clear(h);
+ deallocate(h->buckets);
+ deallocate(h);
+ }
+}
+
+/** Push an entry on the list in the bucket for a given hashcode.
+ *
+ * @param table to add entry to
+ * @param hashcode for the entry
+ * @param entry to add
+ */
+static inline void push_on_bucket(HashTable *table, Hashcode hashcode,
+ HTEntry *entry){
+ HTBucket *bucket;
+ HTEntry *old_head;
+
+ bucket = get_bucket(table, hashcode);
+ old_head = bucket->head;
+ bucket->count++;
+ bucket->head = entry;
+ entry->next = old_head;
+}
+
+/** Change the number of buckets in a hashtable.
+ * No-op if the number of buckets is not positive.
+ * Existing entries are reallocated to buckets based on their hashcodes.
+ * The table is unmodified if the number of buckets cannot be changed.
+ *
+ * @param table hashtable
+ * @param buckets_n new number of buckets
+ * @return 0 on success, error code otherwise
+ */
+int HashTable_set_buckets_n(HashTable *table, int buckets_n){
+ int err = 0;
+ HTBucket *old_buckets = table->buckets;
+ int old_buckets_n = table->buckets_n;
+ int i;
+
+ if(buckets_n <= 0){
+ err = -EINVAL;
+ goto exit;
+ }
+ table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
+ if(!table->buckets){
+ err = -ENOMEM;
+ table->buckets = old_buckets;
+ goto exit;
+ }
+ table->buckets_n = buckets_n;
+ for(i=0; i<old_buckets_n; i++){
+ HTBucket *bucket = old_buckets + i;
+ HTEntry *entry, *next;
+ for(entry = bucket->head; entry; entry = next){
+ next = entry->next;
+ push_on_bucket(table, entry->hashcode, entry);
+ }
+ }
+ deallocate(old_buckets);
+ exit:
+ return err;
+}
+
+/** Adjust the number of buckets so the table is neither too full nor too empty.
+ * The table is unmodified if adjusting fails.
+ *
+ * @param table hash table
+ * @param buckets_min minimum number of buckets (use default if 0 or negative)
+ * @return 0 on success, error code otherwise
+ */
+int HashTable_adjust(HashTable *table, int buckets_min){
+ int buckets_n = 0;
+ int err = 0;
+ if(buckets_min <= 0) buckets_min = HT_BUCKETS_N;
+ if(table->entry_count >= table->buckets_n){
+ // The table is dense - expand it.
+ buckets_n = 2 * table->buckets_n;
+ } else if((table->buckets_n > buckets_min) &&
+ (4 * table->entry_count < table->buckets_n)){
+ // The table is more than minimum size and sparse - shrink it.
+ buckets_n = 2 * table->entry_count;
+ if(buckets_n < buckets_min) buckets_n = buckets_min;
+ }
+ if(buckets_n){
+ err = HashTable_set_buckets_n(table, buckets_n);
+ }
+ return err;
+}
+
+/** Allocate a new entry for a given value.
+ *
+ * @param value to put in the entry
+ * @return entry, or 0 on failure
+ */
+HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){
+ HTEntry *z = ALLOCATE(HTEntry);
+ if(z){
+ z->hashcode = hashcode;
+ z->key = key;
+ z->value = value;
+ }
+ return z;
+}
+
+/** Free an entry.
+ *
+ * @param z entry to free
+ */
+inline void HTEntry_free(HTEntry *z){
+ if(z){
+ deallocate(z);
+ }
+}
+
+/** Free an entry in a hashtable.
+ * The table's entry_free_fn is used is defined, otherwise
+ * the HTEntry itself is freed.
+ *
+ * @param table hashtable
+ * @param entry to free
+ */
+inline void HashTable_free_entry(HashTable *table, HTEntry *entry){
+ if(!entry)return;
+ if(table && table->entry_free_fn){
+ table->entry_free_fn(table, entry);
+ } else {
+ HTEntry_free(entry);
+ }
+}
+
+/** Get the first entry satisfying a test from the bucket for the
+ * given hashcode.
+ *
+ * @param table to look in
+ * @param hashcode indicates the bucket
+ * @param test_fn test to apply to elements
+ * @param arg first argument to calls to test_fn
+ * @return entry found, or 0
+ */
+inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
+ TableTestFn *test_fn, TableArg arg){
+ HTBucket *bucket;
+ HTEntry *entry = 0;
+ HTEntry *next;
+
+ bucket = get_bucket(table, hashcode);
+ for(entry = bucket->head; entry; entry = next){
+ next = entry->next;
+ if(test_fn(arg, table, entry)){
+ break;
+ }
+ }
+ return entry;
+}
+
+/** Test hashtable keys for equality.
+ * Uses the table's key_equal_fn if defined, otherwise pointer equality.
+ *
+ * @param key1 key to compare
+ * @param key2 key to compare
+ * @return 1 if equal, 0 otherwise
+ */
+inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){
+ return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2);
+}
+
+/** Compute the hashcode of a hashtable key.
+ * The table's key_hash_fn is used if defined, otherwise the address of
+ * the key is hashed.
+ *
+ * @param table hashtable
+ * @param key to hash
+ * @return hashcode
+ */
+inline Hashcode HashTable_key_hash(HashTable *table, void *key){
+ return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key));
+}
+
+/** Test if an entry has a given key.
+ *
+ * @param arg containing key to test for
+ * @param table the entry is in
+ * @param entry to test
+ * @return 1 if the entry has the key, 0 otherwise
+ */
+static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){
+ return HashTable_key_equal(table, arg.ptr, entry->key);
+}
+
+/** Get an entry with a given key.
+ *
+ * @param table to search
+ * @param key to look for
+ * @return entry if found, null otherwise
+ */
+#if 0
+inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
+ TableArg arg = { ptr: key };
+ return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg);
+}
+#else
+inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
+ Hashcode hashcode;
+ HTBucket *bucket;
+ HTEntry *entry = 0;
+ HTEntry *next;
+
+ hashcode = HashTable_key_hash(table, key);
+ bucket = get_bucket(table, hashcode);
+ for(entry = bucket->head; entry; entry = next){
+ next = entry->next;
+ if(HashTable_key_equal(table, key, entry->key)){
+ break;
+ }
+ }
+ return entry;
+}
+#endif
+
+/** Get the value of an entry with a given key.
+ *
+ * @param table to search
+ * @param key to look for
+ * @return value if an entry was found, null otherwise
+ */
+inline void * HashTable_get(HashTable *table, void *key){
+ HTEntry *entry = HashTable_get_entry(table, key);
+ return (entry ? entry->value : 0);
+}
+
+/** Print the buckets in a table.
+ *
+ * @param table to print
+ */
+void show_buckets(HashTable *table, IOStream *io){
+ int i,j ;
+ IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n);
+ for(i=0; i<table->buckets_n; i++){
+ if(0 || table->buckets[i].count>0){
+ IOStream_print(io, "bucket %3d %3d %10p ", i,
+ table->buckets[i].count,
+ table->buckets[i].head);
+ for(j = table->buckets[i].count; j>0; j--){
+ IOStream_print(io, "+");
+ }
+ IOStream_print(io, "\n");
+ }
+ }
+ HashTable_print(table, io);
+}
+
+/** Print an entry in a table.
+ *
+ * @param entry to print
+ * @param arg a pointer to an IOStream to print to
+ * @return 0
+ */
+static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){
+ IOStream *io = (IOStream*)arg.ptr;
+ IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n",
+ entry->hashcode % table->buckets_n,
+ entry->hashcode,
+ entry->index,
+ entry, entry->key, entry->value);
+ return 0;
+}
+
+/** Print a hash table.
+ *
+ * @param table to print
+ */
+void HashTable_print(HashTable *table, IOStream *io){
+ IOStream_print(io, "{\n");
+ HashTable_map(table, print_entry, (TableArg){ ptr: io });
+ IOStream_print(io, "}\n");
+}
+/*==========================================================================*/
+
+/** Get the next entry id to use for a table.
+ *
+ * @param table hash table
+ * @return non-zero entry id
+ */
+static inline unsigned long get_next_id(HashTable *table){
+ unsigned long id;
+
+ if(table->next_id == 0){
+ table->next_id = 1;
+ }
+ id = table->next_id++;
+ return id;
+}
+
+/** Add an entry to the bucket for the
+ * given hashcode.
+ *
+ * @param table to insert in
+ * @param hashcode indicates the bucket
+ * @param key to add an entry for
+ * @param value to add an entry for
+ * @return entry on success, 0 on failure
+ */
+inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){
+ HTEntry *entry = HTEntry_new(hashcode, key, value);
+ if(entry){
+ entry->index = get_next_id(table);
+ push_on_bucket(table, hashcode, entry);
+ table->entry_count++;
+ }
+ return entry;
+}
+
+/** Move the front entry for a bucket to the correct point in the bucket order as
+ * defined by the order function. If this is called every time a new entry is added
+ * the bucket will be maintained in sorted order.
+ *
+ * @param table to modify
+ * @param hashcode indicates the bucket
+ * @param order entry comparison function
+ * @return 0 if an entry was moved, 1 if not
+ */
+int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){
+ HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL;
+ HTBucket *bucket;
+ int err = 1;
+
+ bucket = get_bucket(table, hashcode);
+ new_entry = bucket->head;
+ if(!new_entry || !new_entry->next) goto exit;
+ for(entry = new_entry->next; entry; prev = entry, entry = entry->next){
+ if(order(new_entry, entry) <= 0) break;
+ }
+ if(prev){
+ err = 0;
+ bucket->head = new_entry->next;
+ new_entry->next = entry;
+ prev->next = new_entry;
+ }
+ exit:
+ return err;
+}
+
+/** Add an entry to a hashtable.
+ * The entry is added to the bucket for its key's hashcode.
+ *
+ * @param table to insert in
+ * @param key to add an entry for
+ * @param value to add an entry for
+ * @return entry on success, 0 on failure
+ */
+inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){
+ return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value);
+}
+
+
+/** Remove entries satisfying a test from the bucket for the
+ * given hashcode.
+ *
+ * @param table to remove from
+ * @param hashcode indicates the bucket
+ * @param test_fn test to apply to elements
+ * @param arg first argument to calls to test_fn
+ * @return number of entries removed
+ */
+inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
+ TableTestFn *test_fn, TableArg arg){
+ HTBucket *bucket;
+ HTEntry *entry, *prev = 0, *next;
+ int removed_count = 0;
+
+ bucket = get_bucket(table, hashcode);
+ for(entry = bucket->head; entry; entry = next){
+ next = entry->next;
+ if(test_fn(arg, table, entry)){
+ if(prev){
+ prev->next = next;
+ } else {
+ bucket->head = next;
+ }
+ bucket->count--;
+ table->entry_count--;
+ removed_count++;
+ HashTable_free_entry(table, entry);
+ entry = 0;
+ }
+ prev = entry;
+ }
+ return removed_count;
+}
+
+/** Remove entries with a given key.
+ *
+ * @param table to remove from
+ * @param key of entries to remove
+ * @return number of entries removed
+ */
+inline int HashTable_remove(HashTable *table, void *key){
+#if 1
+ Hashcode hashcode;
+ HTBucket *bucket;
+ HTEntry *entry, *prev = 0, *next;
+ int removed_count = 0;
+
+ hashcode = HashTable_key_hash(table, key);
+ bucket = get_bucket(table, hashcode);
+ for(entry = bucket->head; entry; entry = next){
+ next = entry->next;
+ if(HashTable_key_equal(table, key, entry->key)){
+ if(prev){
+ prev->next = next;
+ } else {
+ bucket->head = next;
+ }
+ bucket->count--;
+ table->entry_count--;
+ removed_count++;
+ HashTable_free_entry(table, entry);
+ entry = 0;
+ }
+ prev = entry;
+ }
+ return removed_count;
+#else
+ return HashTable_remove_entry(table, HashTable_key_hash(table, key),
+ has_key, (TableArg){ ptr: key});
+#endif
+}
+
+/** Remove (and free) all the entries in a bucket.
+ *
+ * @param bucket to clear
+ */
+static inline void bucket_clear(HashTable *table, HTBucket *bucket){
+ HTEntry *entry, *next;
+
+ for(entry = bucket->head; entry; entry = next){
+ next = entry->next;
+ HashTable_free_entry(table, entry);
+ }
+ bucket->head = 0;
+ table->entry_count -= bucket->count;
+ bucket->count = 0;
+}
+
+/** Remove (and free) all the entries in a table.
+ *
+ * @param table to clear
+ */
+void HashTable_clear(HashTable *table){
+ int i, n = table->buckets_n;
+
+ for(i=0; i<n; i++){
+ bucket_clear(table, table->buckets + i);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XUTIL_HASH_TABLE_H_
+#define _XUTIL_HASH_TABLE_H_
+
+#include "iostream.h"
+
+typedef unsigned long Hashcode;
+
+/** Type used to pass parameters to table functions. */
+typedef union TableArg {
+ unsigned long ul;
+ void *ptr;
+} TableArg;
+
+/** An entry in a bucket list. */
+typedef struct HTEntry {
+ /** Hashcode of the entry's key. */
+ Hashcode hashcode;
+ /** Identifier for this entry in the table. */
+ int index;
+ /** The key for this entry. */
+ void *key;
+ /** The value in this entry. */
+ void *value;
+ /** The next entry in the list. */
+ struct HTEntry *next;
+} HTEntry;
+
+/** A bucket in a rule table. */
+typedef struct HTBucket {
+ /** Number of entries in the bucket. */
+ int count;
+ /** First entry in the bucket (may be null). */
+ HTEntry *head;
+} HTBucket;
+
+/** Default number of buckets in a hash table.
+ * You want enough buckets so the lists in the buckets will typically be short.
+ * It's a good idea if this is prime, since that will help to spread hashcodes
+ * around the table.
+ */
+//#define HT_BUCKETS_N 1
+//#define HT_BUCKETS_N 3
+//#define HT_BUCKETS_N 7
+//#define HT_BUCKETS_N 17
+//#define HT_BUCKETS_N 97
+//#define HT_BUCKETS_N 211
+//#define HT_BUCKETS_N 401
+#define HT_BUCKETS_N 1021
+
+typedef struct HashTable HashTable;
+
+/** Type for a function used to select table entries. */
+typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry);
+
+/** Type for a function to map over table entries. */
+typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry);
+
+/** Type for a function to free table entries. */
+typedef void TableFreeFn(HashTable *table, HTEntry *entry);
+
+/** Type for a function to hash table keys. */
+typedef Hashcode TableHashFn(void *key);
+
+/** Type for a function to test table keys for equality. */
+typedef int TableEqualFn(void *key1, void *key2);
+
+/** Type for a function to order table entries. */
+typedef int TableOrderFn(HTEntry *e1, HTEntry *e2);
+
+/** General hash table.
+ * A hash table with a list in each bucket.
+ * Functions can be supplied for freeing entries, hashing keys, and comparing keys.
+ * These all default to 0, when default behaviour treating keys as integers is used.
+ */
+struct HashTable {
+ /** Flag indicating whether the table has been initialised. */
+ int init_done;
+ /** Next value for the id field in inserted rules. */
+ unsigned long next_id;
+ /** Number of buckets in the bucket array. */
+ int buckets_n;
+ /** Array of buckets, each with its own list. */
+ HTBucket *buckets;
+ /** Number of entries in the table. */
+ int entry_count;
+ /** Function to free keys and values in entries. */
+ TableFreeFn *entry_free_fn;
+ /** Function to hash keys. */
+ TableHashFn *key_hash_fn;
+ /** Function to compare keys for equality. */
+ TableEqualFn *key_equal_fn;
+ /** Place for the user of the table to hang extra data. */
+ void *user_data;
+};
+
+extern HashTable *HashTable_new(int bucket_n);
+extern void HashTable_free(HashTable *table);
+extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value);
+extern void HTEntry_free(HTEntry *entry);
+extern int HashTable_set_bucket_n(HashTable *table, int bucket_n);
+extern void HashTable_clear(HashTable *table);
+extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value);
+extern HTEntry * HashTable_get_entry(HashTable *table, void *key);
+extern HTEntry * HashTable_add(HashTable *table, void *key, void *value);
+extern void * HashTable_get(HashTable *table, void *key);
+extern int HashTable_remove(HashTable *table, void *key);
+extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
+ TableTestFn *test_fn, TableArg arg);
+extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
+ TableTestFn *test_fn, TableArg arg);
+//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg);
+extern void HashTable_print(HashTable *table, IOStream *out);
+extern int HashTable_set_buckets_n(HashTable *table, int buckets_n);
+extern int HashTable_adjust(HashTable *table, int buckets_min);
+extern void pseudo_des(unsigned long *pleft, unsigned long *pright);
+extern Hashcode hash_string(char *s);
+
+extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order);
+
+/** Control whether to use hashing based on DES or simple
+ * hashing. DES hashing is `more random' but much more expensive.
+ */
+#define HASH_PSEUDO_DES 0
+
+/** Hash a long using a quick and dirty linear congruential random number generator.
+ * See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator".
+ *
+ * @param a value to hash
+ * @return hashed input
+ */
+static inline unsigned long lcrng_hash(unsigned long a){
+ return (1664525L * a + 1013904223L);
+}
+
+/** Hash an unsigned long.
+ *
+ * @param a input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_ul(unsigned long a){
+#if HASH_PSEUDO_DES
+ unsigned long left = a;
+ unsigned long right = 0L;
+ pseudo_des(&left, &right);
+ return right;
+#else
+ a = lcrng_hash(a);
+ a = lcrng_hash(a);
+ return a;
+#endif
+}
+
+/** Hash two unsigned longs together.
+ *
+ * @param a input to hash
+ * @param b input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_2ul(unsigned long a, unsigned long b){
+#if HASH_PSEUDO_DES
+ unsigned long left = a;
+ unsigned long right = b;
+ pseudo_des(&left, &right);
+ return right;
+#else
+ a = lcrng_hash(a);
+ a ^= b;
+ a = lcrng_hash(a);
+ return a;
+#endif
+}
+
+/** Hash a hashcode and an unsigned long together.
+ *
+ * @param a input hashcode
+ * @param b input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_hul(Hashcode a, unsigned long b){
+#if HASH_PSEUDO_DES
+ unsigned long left = a;
+ unsigned long right = b;
+ pseudo_des(&left, &right);
+ return right;
+#else
+ a ^= b;
+ a = lcrng_hash(a);
+ return a;
+#endif
+}
+
+/** Macro to declare variables for HashTable_for_each() to use.
+ *
+ * @param entry variable that is set to entries in the table
+ */
+#define HashTable_for_decl(entry) \
+ HashTable *_var_table; \
+ HTBucket *_var_bucket; \
+ HTBucket *_var_end; \
+ HTEntry *_var_next; \
+ HTEntry *entry
+
+/** Macro to iterate over the entries in a hashtable.
+ * Must be in a scope where HashTable_for_decl() has been used to declare
+ * variables for it to use.
+ * The variable 'entry' is iterated over entries in the table.
+ * The code produced is syntactically a loop, so it must be followed by
+ * a loop body, typically some statements in braces:
+ * HashTable_for_each(entry, table){ ...loop body... }
+ *
+ * HashTable_for_each() and HashTable_for_decl() cannot be used for nested
+ * loops as variables will clash.
+ *
+ * @note The simplest way to code a direct loop over the entries in a hashtable
+ * is to use a loop over the buckets, with a nested loop over the entries
+ * in a bucket. Using this approach in a macro means the macro contains
+ * an opening brace, and calls to it must be followed by 2 braces!
+ * To avoid this the code has been restructured so that it is a for loop.
+ * So that statements could be used in the test expression of the for loop,
+ * we have used the gcc statement expression extension ({ ... }).
+ *
+ * @param entry variable to iterate over the entries
+ * @param table to iterate over (non-null)
+ */
+#define HashTable_for_each(entry, table) \
+ _var_table = table; \
+ _var_bucket = _var_table->buckets; \
+ _var_end = _var_bucket + _var_table->buckets_n; \
+ for(entry=0, _var_next=0; \
+ ({ if(_var_next){ \
+ entry = _var_next; \
+ _var_next = entry->next; \
+ } else { \
+ while(_var_bucket < _var_end){ \
+ entry = _var_bucket->head; \
+ _var_bucket++; \
+ if(entry){ \
+ _var_next = entry->next; \
+ break; \
+ } \
+ } \
+ }; \
+ entry; }); \
+ entry = _var_next )
+
+/** Map a function over the entries in a table.
+ * Mapping stops when the function returns a non-zero value.
+ * Uses the gcc statement expression extension ({ ... }).
+ *
+ * @param table to map over
+ * @param fn function to apply to entries
+ * @param arg first argument to call the function with
+ * @return 0 if fn always returned 0, first non-zero value otherwise
+ */
+#define HashTable_map(table, fn, arg) \
+ ({ HashTable_for_decl(_var_entry); \
+ TableArg _var_arg = arg; \
+ int _var_value = 0; \
+ HashTable_for_each(_var_entry, table){ \
+ if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \
+ } \
+ _var_value; })
+
+/** Cast x to the type for a key or value in a hash table.
+ * This avoids compiler warnings when using short integers
+ * as keys or values (especially on 64-bit platforms).
+ */
+#define HKEY(x) ((void*)(unsigned long)(x))
+
+/** Cast x from the type for a key or value in a hash table.
+ * to an unsigned long. This avoids compiler warnings when using
+ * short integers as keys or values (especially on 64-bit platforms).
+ */
+#define HVAL(x) ((unsigned long)(x))
+
+#endif /* !_XUTIL_HASH_TABLE_H_ */
--- /dev/null
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ * Lexical analysis.
+ */
+
+#include "sys_string.h"
+#include "lexis.h"
+#include <errno.h>
+
+/** Check if a value lies in a (closed) range.
+ *
+ * @param x value to test
+ * @param lo low end of the range
+ * @param hi high end of the range
+ * @return 1 if x is in the interval [lo, hi], 0 otherwise
+ */
+inline static int in_range(int x, int lo, int hi){
+ return (lo <= x) && (x <= hi);
+}
+
+/** Determine if a string is an (unsigned) decimal number.
+ *
+ * @param s pointer to characters to test
+ * @param n length of string
+ * @return 1 if s is a decimal number, 0 otherwise.
+ */
+int is_decimal_number(const char *s, int n){
+ int i;
+ if(n <= 0)return 0;
+ for(i = 0; i < n; i++){
+ if(!in_decimal_digit_class(s[i])) return 0;
+ }
+ return 1;
+}
+
+/** Determine if a string is a hex number.
+ * Hex numbers are 0, or start with 0x or 0X followed
+ * by a non-zero number of hex digits (0-9,a-f,A-F).
+ *
+ * @param s pointer to characters to test
+ * @param n length of string
+ * @return 1 if s is a hex number, 0 otherwise.
+ */
+int is_hex_number(const char *s, int n){
+ int i;
+ if(n <= 0) return 0;
+ if(n == 1){
+ return s[0]=='0';
+ }
+ if(n <= 3) return 0;
+ if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0;
+ for(i = 2; i < n; i++){
+ if(!in_hex_digit_class(s[i])) return 0;
+ }
+ return 1;
+}
+
+/** Test if a string matches a keyword.
+ * The comparison is case-insensitive.
+ * The comparison fails if either argument is null.
+ *
+ * @param s string
+ * @param k keyword
+ * @return 1 if they match, 0 otherwise
+ */
+int is_keyword(const char *s, const char *k){
+ return s && k && !strcasecmp(s, k);
+}
+
+/** Test if a string matches a character.
+ *
+ * @param s string
+ * @param c character (non-null)
+ * @return 1 if s contains exactly c, 0 otherwise
+ */
+int is_keychar(const char *s, char c){
+ return c && (s[0] == c) && !s[1];
+}
--- /dev/null
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XUTIL_LEXIS_H_
+#define _XUTIL_LEXIS_H_
+
+#include "sys_string.h"
+
+#ifdef __KERNEL__
+# include <linux/ctype.h>
+#else
+# include <ctype.h>
+#endif
+
+/** @file
+ * Lexical analysis.
+ */
+
+/** Class of characters treated as space. */
+#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 })
+
+/** Class of separator characters. */
+#define sep_class "{}()<>[]@!;"
+
+#define comment_class "#"
+
+/** Determine if a character is in a given class.
+ *
+ * @param c character to test
+ * @param s null-terminated string of characters in the class
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_class(int c, const char *s){
+ return s && (strchr(s, c) != 0);
+}
+
+/** Determine if a character is in the space class.
+ *
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_space_class(int c){
+ return in_class(c, space_class);
+}
+
+static inline int in_comment_class(int c){
+ return in_class(c, comment_class);
+}
+
+/** Determine if a character is in the separator class.
+ * Separator characters terminate tokens, and do not need space
+ * to separate them.
+ *
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_sep_class(int c){
+ return in_class(c, sep_class);
+}
+
+/** Determine if a character is in the alpha class.
+ *
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_alpha_class(int c){
+ return isalpha(c);
+}
+
+/** Determine if a character is in the octal digit class.
+ *
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_octal_digit_class(int c){
+ return '0' <= c && c <= '7';
+}
+
+/** Determine if a character is in the decimal digit class.
+ *
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_decimal_digit_class(int c){
+ return isdigit(c);
+}
+
+/** Determine if a character is in the hex digit class.
+ *
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_hex_digit_class(int c){
+ return isdigit(c) || in_class(c, "abcdefABCDEF");
+}
+
+
+static inline int in_string_quote_class(int c){
+ return in_class(c, "'\"");
+}
+
+static inline int in_printable_class(int c){
+ return ('A' <= c && c <= 'Z')
+ || ('a' <= c && c <= 'z')
+ || ('0' <= c && c <= '9')
+ || in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~");
+}
+
+extern int is_decimal_number(const char *s, int n);
+extern int is_hex_number(const char *s, int n);
+extern int is_keyword(const char *s, const char *k);
+extern int is_keychar(const char *s, char c);
+
+#endif /* !_XUTIL_LEXIS_H_ */
--- /dev/null
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ * An IOStream implementation using LZI to provide compression and decompression.
+ * This is designed to provide compression without output latency.
+ * Flushing an LZI stream flushes all pending data to the underlying stream.
+ * This is essential for stream-based (e.g. networked) applications.
+ *
+ * A compressed data stream is a sequence of blocks.
+ * Each block is the block size followed by the compressed data.
+ * The last block has size zero.
+ * Sizes are 4-byte unsigned in network order.
+ *
+ * This format allows compressed data to be read from a stream without reading
+ * past the logical end of compressed data.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "zlib.h"
+
+#include "allocate.h"
+#include "lzi_stream.h"
+#include "file_stream.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZI>%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZI>%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args)
+
+static int lzi_read(IOStream *s, void *buf, size_t n);
+static int lzi_write(IOStream *s, const void *buf, size_t n);
+static int lzi_error(IOStream *s);
+static int lzi_close(IOStream *s);
+static void lzi_free(IOStream *s);
+static int lzi_flush(IOStream *s);
+
+enum {
+ LZI_WRITE = 1,
+ LZI_READ = 2,
+};
+
+/** Methods used by a gzFile* IOStream. */
+static const IOMethods lzi_methods = {
+ read: lzi_read,
+ write: lzi_write,
+ error: lzi_error,
+ close: lzi_close,
+ free: lzi_free,
+ flush: lzi_flush,
+};
+
+#define BUFFER_SIZE (512 * 1024)
+
+typedef struct LZIState {
+ z_stream zstream;
+ void *inbuf;
+ uint32_t inbuf_size;
+ void *outbuf;
+ uint32_t outbuf_size;
+ /** Underlying stream for I/O. */
+ IOStream *io;
+ /** Flags. */
+ int flags;
+ /** Error indicator. */
+ int error;
+ int eof;
+ int plain_bytes;
+ int comp_bytes;
+ int zstream_initialized;
+ int flushed;
+} LZIState;
+
+static inline int LZIState_writeable(LZIState *s){
+ return (s->flags & LZI_WRITE) != 0;
+}
+
+static inline int LZIState_readable(LZIState *s){
+ return (s->flags & LZI_READ) != 0;
+}
+
+void LZIState_free(LZIState *z){
+ if(!z) return;
+ if(z->zstream_initialized){
+ if(LZIState_writeable(z)){
+ deflateEnd(&z->zstream);
+ } else if(LZIState_readable(z)){
+ inflateEnd(&z->zstream);
+ }
+ }
+ deallocate(z->inbuf);
+ deallocate(z->outbuf);
+ deallocate(z);
+}
+
+static int mode_flags(const char *mode, int *flags){
+ int err = 0;
+ int r=0, w=0;
+ if(!mode){
+ err = -EINVAL;
+ goto exit;
+ }
+ for(; *mode; mode++){
+ if(*mode == 'w') w = 1;
+ if(*mode == 'r') r = 1;
+ }
+ if(r + w != 1){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(r) *flags |= LZI_READ;
+ if(w) *flags |= LZI_WRITE;
+ exit:
+ return err;
+}
+
+/** Get the stream state.
+ *
+ * @param s lzi stream
+ * @return stream state.
+ */
+static inline LZIState * lzi_state(IOStream *io){
+ return (LZIState*)io->data;
+}
+
+IOStream *lzi_stream_io(IOStream *io){
+ LZIState *s = lzi_state(io);
+ return s->io;
+}
+
+static inline void set_error(LZIState *s, int err){
+ if(err < 0 && !s->error){
+ s->error = err;
+ }
+}
+
+static int zerror(LZIState *s, int err){
+ if(err){
+ //dprintf("> err=%d\n", err);
+ if(err < 0) set_error(s, -EIO);
+ }
+ return s->error;
+}
+
+int lzi_stream_plain_bytes(IOStream *io){
+ LZIState *s = lzi_state(io);
+ return s->plain_bytes;
+}
+
+int lzi_stream_comp_bytes(IOStream *io){
+ LZIState *s = lzi_state(io);
+ return s->comp_bytes;
+}
+
+float lzi_stream_ratio(IOStream *io){
+ LZIState *s = lzi_state(io);
+ float ratio = 0.0;
+ if(s->comp_bytes){
+ ratio = ((float) s->comp_bytes)/((float) s->plain_bytes);
+ }
+ return ratio;
+}
+
+static int alloc(void **p, int n){
+ *p = allocate(n);
+ return (p ? 0 : -ENOMEM);
+}
+
+LZIState * LZIState_new(IOStream *io, int flags){
+ int err = -ENOMEM;
+ int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest.
+ int zstrategy = Z_DEFAULT_STRATEGY;
+ int zwindow = MAX_WBITS;
+ int zmemory = 8;
+ LZIState *z = ALLOCATE(LZIState);
+
+ //dprintf(">\n");
+ if(!z) goto exit;
+ z->io = io;
+ z->flags = flags;
+
+ if(LZIState_writeable(z)){
+ z->outbuf_size = BUFFER_SIZE;
+ /* windowBits is passed < 0 to suppress zlib header */
+ err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy);
+ if (err != Z_OK) goto exit;
+ z->zstream_initialized = 1;
+ err = alloc(&z->outbuf, z->outbuf_size);
+ if(err) goto exit;
+ z->zstream.next_out = z->outbuf;
+ z->zstream.avail_out = z->outbuf_size;
+ } else {
+ z->inbuf_size = BUFFER_SIZE;
+ err = alloc(&z->inbuf, z->inbuf_size);
+ if(err) goto exit;
+ ///z->zstream.next_in = z->inbuf;
+
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+ * present after the compressed stream.
+ */
+ err = inflateInit2(&z->zstream, -zwindow);
+ if(err != Z_OK) goto exit;
+ z->zstream_initialized = 1;
+ }
+
+ exit:
+ if(err){
+ LZIState_free(z);
+ z = NULL;
+ }
+ //dprintf("< z=%p\n", z);
+ return z;
+}
+
+int read_block(LZIState *s){
+ int err = 0, k = 0;
+ //dprintf(">\n");
+ if(s->eof) goto exit;
+ err = unmarshal_uint32(s->io, &k);
+ if(err) goto exit;
+ if(k > s->inbuf_size){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(k){
+ err = unmarshal_bytes(s->io, s->inbuf, k);
+ if(err) goto exit;
+ } else {
+ s->eof = 1;
+ }
+ s->zstream.avail_in = k;
+ s->zstream.next_in = s->inbuf;
+ s->comp_bytes += 4;
+ s->comp_bytes += k;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int write_block(LZIState *s){
+ int err = 0;
+ int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf);
+ //int k2 = s->outbuf_size - s->zstream.avail_out;
+ //dprintf("> k=%d k2=%d\n", k, k2);
+ if(!k) goto exit;
+ err = marshal_uint32(s->io, k);
+ if(err) goto exit;
+ err = marshal_bytes(s->io, s->outbuf, k);
+ if(err) goto exit;
+ s->zstream.next_out = s->outbuf;
+ s->zstream.avail_out = s->outbuf_size;
+ s->comp_bytes += 4;
+ s->comp_bytes += k;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int write_terminator(LZIState *s){
+ int err = 0;
+ char c = 0;
+ err = marshal_uint32(s->io, 1);
+ if(err) goto exit;
+ err = marshal_bytes(s->io, &c, 1);
+ if(err) goto exit;
+ err = marshal_uint32(s->io, 0);
+ if(err) goto exit;
+ s->comp_bytes += 9;
+ exit:
+ return err;
+}
+
+/** Write to the underlying stream using fwrite();
+ *
+ * @param io destination
+ * @param buf data
+ * @param n number of bytes to write
+ * @return number of bytes written
+ */
+static int lzi_write(IOStream *io, const void *buf, size_t n){
+ int err = 0;
+ LZIState *s = lzi_state(io);
+
+ //dprintf("> buf=%p n=%d\n", buf, n);
+ if(!LZIState_writeable(s)){
+ err = -EINVAL;
+ goto exit;
+ }
+ s->flushed = 0;
+ s->zstream.next_in = (void*)buf;
+ s->zstream.avail_in = n;
+ while(s->zstream.avail_in){
+ if(s->zstream.avail_out == 0){
+ err = write_block(s);
+ if(err) goto exit;
+ }
+ //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
+ err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH));
+ //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
+ if(err) goto exit;
+ }
+ err = n;
+ s->plain_bytes += n;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+
+/** Read from the underlying stream.
+ *
+ * @param io input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return number of bytes read
+ */
+static int lzi_read(IOStream *io, void *buf, size_t n){
+ int err, zerr;
+ LZIState *s = lzi_state(io);
+
+ //dprintf("> n=%d\n", n);
+ if(!LZIState_readable(s)){
+ err = -EINVAL;
+ goto exit;
+ }
+ s->zstream.next_out = buf;
+ s->zstream.avail_out = n;
+ while(s->zstream.avail_out){
+ if(s->zstream.avail_in == 0){
+ err = read_block(s);
+ }
+ //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ zerr = inflate(&s->zstream, Z_NO_FLUSH);
+ //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ if(zerr == Z_STREAM_END) break;
+ //dprintf("> zerr=%d\n", zerr);
+ err = zerror(s, zerr);
+ if(err) goto exit;
+ }
+ err = n - s->zstream.avail_out;
+ s->plain_bytes += err;
+ exit:
+ set_error(s, err);
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+static int flush_output(LZIState *s, int mode){
+ int err = 0, zerr;
+ int done = 0;
+ int avail_out_old;
+
+ //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ if(s->flushed == 1 + mode) goto exit;
+ //s->zstream.avail_in = 0; /* should be zero already anyway */
+ for(;;){
+ // Write any available output.
+ if(done || s->zstream.avail_out == 0){
+ err = write_block(s);
+ if(err) goto exit;
+ if(done) break;
+ }
+ //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ avail_out_old = s->zstream.avail_out;
+ zerr = deflate(&s->zstream, mode);
+ err = zerror(s, zerr);
+ //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+ //dprintf("> deflate=%d\n", err);
+ //done = (s->zstream.avail_out != 0);
+ //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old);
+ if(0 && mode == Z_FINISH){
+ done = (zerr == Z_STREAM_END);
+ } else {
+ done = (s->zstream.avail_in == 0)
+ //&& (s->zstream.avail_out == avail_out_old)
+ && (s->zstream.avail_out != 0);
+ }
+ }
+ s->flushed = 1 + mode;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Flush any pending input to the underlying stream.
+ *
+ * @param s lzi stream
+ * @return 0 on success, error code otherwise
+ */
+static int lzi_flush(IOStream *io){
+ int err = 0;
+ LZIState *s = lzi_state(io);
+ //dprintf(">\n");
+ if(!LZIState_writeable(s)){
+ err = -EINVAL;
+ goto exit;
+ }
+ err = flush_output(s, Z_SYNC_FLUSH);
+ if(err) goto exit;
+ err = IOStream_flush(s->io);
+ exit:
+ set_error(s, err);
+ //dprintf("< err=%d\n", err);
+ return (err < 0 ? err : 0);
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s lzi stream
+ * @return code if has an error, 0 otherwise
+ */
+static int lzi_error(IOStream *s){
+ int err = 0;
+ LZIState *state = lzi_state(s);
+ err = state->error;
+ if(err) goto exit;
+ err = IOStream_error(state->io);
+ exit:
+ return err;
+}
+
+/** Close an lzi stream.
+ *
+ * @param s lzi stream to close
+ * @return result of the close
+ */
+static int lzi_close(IOStream *io){
+ int err = 0;
+ LZIState *s = lzi_state(io);
+ if(LZIState_writeable(s)){
+ err = flush_output(s, Z_FINISH);
+ if(err) goto exit;
+ err = write_terminator(s);
+ if(err) goto exit;
+ err = IOStream_flush(s->io);
+ }
+ exit:
+ err = IOStream_close(s->io);
+ set_error(s, err);
+ return err;
+}
+
+/** Free an lzi stream.
+ *
+ * @param s lzi stream
+ */
+static void lzi_free(IOStream *s){
+ LZIState *state = lzi_state(s);
+ IOStream_free(state->io);
+ LZIState_free(state);
+ s->data = NULL;
+}
+
+/** Create an lzi stream for an IOStream.
+ *
+ * @param io stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *lzi_stream_new(IOStream *io, const char *mode){
+ int err = -ENOMEM;
+ int flags = 0;
+ IOStream *zio = NULL;
+ LZIState *state = NULL;
+
+ zio = ALLOCATE(IOStream);
+ if(!zio) goto exit;
+ err = mode_flags(mode, &flags);
+ if(err) goto exit;
+ state = LZIState_new(io, flags);
+ if(!state) goto exit;
+ err = 0;
+ zio->data = state;
+ zio->methods = &lzi_methods;
+ exit:
+ if(err){
+ if(state) LZIState_free(state);
+ if(zio) deallocate(zio);
+ zio = NULL;
+ }
+ return zio;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *lzi_stream_fdopen(int fd, const char *mode){
+ int err = -ENOMEM;
+ IOStream *io = NULL, *zio = NULL;
+ io = file_stream_fdopen(fd, mode);
+ if(!io) goto exit;
+ zio = lzi_stream_new(io, mode);
+ if(!io) goto exit;
+ err = 0;
+ exit:
+ if(err){
+ IOStream_free(io);
+ IOStream_free(zio);
+ zio = NULL;
+ }
+ return zio;
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XUTIL_LZI_STREAM_H_
+#define _XUTIL_LZI_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+
+extern IOStream *lzi_stream_new(IOStream *io, const char *mode);
+extern IOStream *lzi_stream_fopen(const char *file, const char *mode);
+extern IOStream *lzi_stream_fdopen(int fd, const char *mode);
+extern IOStream *lzi_stream_io(IOStream *zio);
+
+extern int lzi_stream_plain_bytes(IOStream *io);
+extern int lzi_stream_comp_bytes(IOStream *io);
+extern float lzi_stream_ratio(IOStream *io);
+
+#endif
+#endif /* !_XUTIL_LZI_STREAM_H_ */
--- /dev/null
+#include <errno.h>
+#include "sys_net.h"
+#include "allocate.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN] %s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO] %s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args)
+
+
+#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0]))
+
+/* Messages are coded as msgid followed by message fields.
+ * Initial message on any channel is hello - so can check version
+ * compatibility.
+ *
+ * char* -> uint16_t:n <n bytes>
+ * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes).
+ * optional fields go as '1' <val> or '0' (the 0/1 is 1 byte).
+ * lists go as ('1' <elt>)* '0'
+ */
+
+int marshal_flush(IOStream *io){
+ int err = 0;
+ err = IOStream_flush(io);
+ return err;
+}
+
+int marshal_bytes(IOStream *io, void *s, uint32_t s_n){
+ int err = 0;
+ int n;
+ n = IOStream_write(io, s, s_n);
+ if(n < 0){
+ err = n;
+ } else if (n < s_n){
+ wprintf("> Wanted %d, got %d\n", s_n, n);
+ err = -EIO;
+ }
+ return err;
+}
+
+int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){
+ int err = 0;
+ int n;
+ //dprintf("> s_n=%d\n", s_n);
+ n = IOStream_read(io, s, s_n);
+ //dprintf("> n=%d\n", n);
+ if(n < 0){
+ err = n;
+ } else if(n < s_n){
+ wprintf("> Wanted %d, got %d\n", s_n, n);
+ err = -EIO;
+ }
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int marshal_uint8(IOStream *io, uint8_t x){
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint8(IOStream *io, uint8_t *x){
+ return unmarshal_bytes(io, x, sizeof(*x));
+}
+
+int marshal_uint16(IOStream *io, uint16_t x){
+ x = htons(x);
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint16(IOStream *io, uint16_t *x){
+ int err = 0;
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ *x = ntohs(*x);
+ return err;
+}
+
+int marshal_int32(IOStream *io, int32_t x){
+ int err = 0;
+ //dprintf("> x=%d\n", x);
+ x = htonl(x);
+ err = marshal_bytes(io, &x, sizeof(x));
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unmarshal_int32(IOStream *io, int32_t *x){
+ int err = 0;
+ //dprintf(">\n");
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ *x = ntohl(*x);
+ //dprintf("< err=%d x=%d\n", err, *x);
+ return err;
+}
+
+int marshal_uint32(IOStream *io, uint32_t x){
+ int err = 0;
+ //dprintf("> x=%u\n", x);
+ x = htonl(x);
+ err = marshal_bytes(io, &x, sizeof(x));
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unmarshal_uint32(IOStream *io, uint32_t *x){
+ int err = 0;
+ //dprintf(">\n");
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ *x = ntohl(*x);
+ //dprintf("< err=%d x=%u\n", err, *x);
+ return err;
+}
+
+int marshal_uint64(IOStream *io, uint64_t x){
+ int err;
+ err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff));
+ if(err) goto exit;
+ err = marshal_uint32(io, (uint32_t) ( x & 0xffffffff));
+ exit:
+ return err;
+}
+
+int unmarshal_uint64(IOStream *io, uint64_t *x){
+ int err = 0;
+ uint32_t hi, lo;
+ err = unmarshal_uint32(io, &hi);
+ if(err) goto exit;
+ err = unmarshal_uint32(io, &lo);
+ *x = (((uint64_t) hi) << 32) | lo;
+ exit:
+ return err;
+}
+
+int marshal_net16(IOStream *io, net16_t x){
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net16(IOStream *io, net16_t *x){
+ int err = 0;
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ return err;
+}
+
+int marshal_net32(IOStream *io, net32_t x){
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net32(IOStream *io, net32_t *x){
+ int err = 0;
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ return err;
+}
+
+int marshal_string(IOStream *io, char *s, uint32_t s_n){
+ int err;
+ //dprintf("> s=%s\n", s);
+ err = marshal_uint32(io, s_n);
+ if(err) goto exit;
+ err = marshal_bytes(io, s, s_n);
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unmarshal_string(IOStream *io, char *s, uint32_t s_n){
+ int err = 0, val_n = 0;
+ //dprintf(">\n");
+ err = unmarshal_uint32(io, &val_n);
+ if(err) goto exit;
+ if(val_n >= s_n){
+ err = -EINVAL;
+ goto exit;
+ }
+ err = unmarshal_bytes(io, s, val_n);
+ if(err) goto exit;
+ s[val_n] = '\0';
+ exit:
+ //dprintf("< err=%d s=%s\n", err, s);
+ return err;
+}
+
+int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){
+ int err = 0, val_n = 0;
+ char *val = NULL;
+ //dprintf(">\n");
+ err = unmarshal_uint32(io, &val_n);
+ if(err) goto exit;
+ val = allocate(val_n + 1);
+ if(!val){
+ err = -ENOMEM;
+ goto exit;
+ }
+ err = unmarshal_bytes(io, val, val_n);
+ if(err) goto exit;
+ val[val_n] = '\0';
+ exit:
+ if(err){
+ if(val) deallocate(val);
+ val = NULL;
+ val_n = 0;
+ }
+ *s = val;
+ if(s_n) *s_n = val_n;
+ //dprintf("< err=%d s=%s\n", err, *s);
+ return err;
+}
--- /dev/null
+#ifndef _XUTIL_MARSHAL_H_
+#define _XUTIL_MARSHAL_H_
+
+#include "iostream.h"
+
+/** A 16-bit uint in network order, e.g. a port number. */
+typedef uint16_t net16_t;
+
+/** A 32-bit uint in network order, e.g. an IP address. */
+typedef uint32_t net32_t;
+
+extern int marshal_flush(IOStream *io);
+
+extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n);
+extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n);
+
+extern int marshal_uint8(IOStream *io, uint8_t x);
+extern int unmarshal_uint8(IOStream *io, uint8_t *x);
+
+extern int marshal_uint16(IOStream *io, uint16_t x);
+extern int unmarshal_uint16(IOStream *io, uint16_t *x);
+
+extern int marshal_uint32(IOStream *io, uint32_t x);
+extern int unmarshal_uint32(IOStream *io, uint32_t *x);
+
+extern int marshal_int32(IOStream *io, int32_t x);
+extern int unmarshal_int32(IOStream *io, int32_t *x);
+
+extern int marshal_uint64(IOStream *io, uint64_t x);
+extern int unmarshal_uint64(IOStream *io, uint64_t *x);
+
+extern int marshal_net16(IOStream *io, net16_t x);
+extern int unmarshal_net16(IOStream *io, net16_t *x);
+
+extern int marshal_net32(IOStream *io, net32_t x);
+extern int unmarshal_net32(IOStream *io, net32_t *x);
+
+extern int marshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n);
+
+#endif /* ! _XUTIL_MARSHAL_H_ */
--- /dev/null
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "select.h"
+
+/** Zero all the file descriptor sets.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_zero(SelectSet *set){
+ set->n = 0;
+ FD_ZERO(&set->rd);
+ FD_ZERO(&set->wr);
+ FD_ZERO(&set->er);
+}
+
+/** Add a file descriptor to the write set.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_add_read(SelectSet *set, int fd){
+ FD_SET(fd, &set->rd);
+ if(fd > set->n) set->n = fd;
+}
+
+/** Add a file descriptor to the write set.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_add_write(SelectSet *set, int fd){
+ FD_SET(fd, &set->wr);
+ if(fd > set->n) set->n = fd;
+}
+
+/** Select on file descriptors.
+ *
+ * @param set select set
+ * @param timeout timeout (may be NULL for no timeout)
+ * @return 0 on success, -1 otherwise
+ */
+int SelectSet_select(SelectSet *set, struct timeval *timeout){
+ return select(set->n+1, &set->rd, &set->wr, &set->er, timeout);
+}
--- /dev/null
+#ifndef _XFRD_SELECT_H_
+#define _XFRD_SELECT_H_
+
+/** Set of file descriptors for select.
+ */
+typedef struct SelectSet {
+ int n;
+ fd_set rd, wr, er;
+} SelectSet;
+
+extern void SelectSet_zero(SelectSet *set);
+extern void SelectSet_add_read(SelectSet *set, int fd);
+extern void SelectSet_add_write(SelectSet *set, int fd);
+extern int SelectSet_select(SelectSet *set, struct timeval *timeout);
+
+#endif /* ! _XFRD_SELECT_H_ */
--- /dev/null
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include "sys_string.h"
+#include "lexis.h"
+#include "sys_net.h"
+#include "hash_table.h"
+#include "sxpr.h"
+
+#include <errno.h>
+#undef free
+
+/** @file
+ * General representation of sxprs.
+ * Includes print, equal, and free functions for the sxpr types.
+ *
+ * Zero memory containing an Sxpr will have the value ONONE - this is intentional.
+ * When a function returning an sxpr cannot allocate memory we return ONOMEM.
+ *
+ */
+
+static int atom_print(IOStream *io, Sxpr obj, unsigned flags);
+static int atom_equal(Sxpr x, Sxpr y);
+static void atom_free(Sxpr obj);
+
+static int string_print(IOStream *io, Sxpr obj, unsigned flags);
+static int string_equal(Sxpr x, Sxpr y);
+static void string_free(Sxpr obj);
+
+static int cons_print(IOStream *io, Sxpr obj, unsigned flags);
+static int cons_equal(Sxpr x, Sxpr y);
+static void cons_free(Sxpr obj);
+
+static int null_print(IOStream *io, Sxpr obj, unsigned flags);
+static int none_print(IOStream *io, Sxpr obj, unsigned flags);
+static int int_print(IOStream *io, Sxpr obj, unsigned flags);
+static int bool_print(IOStream *io, Sxpr obj, unsigned flags);
+
+/** Type definitions. */
+static SxprType types[1024] = {
+ [T_NONE] { type: T_NONE, name: "none", print: none_print },
+ [T_NULL] { type: T_NULL, name: "null", print: null_print },
+ [T_UINT] { type: T_UINT, name: "int", print: int_print, },
+ [T_BOOL] { type: T_BOOL, name: "bool", print: bool_print, },
+ [T_ATOM] { type: T_ATOM, name: "atom", print: atom_print,
+ pointer: TRUE,
+ free: atom_free,
+ equal: atom_equal,
+ },
+ [T_STRING] { type: T_STRING, name: "string", print: string_print,
+ pointer: TRUE,
+ free: string_free,
+ equal: string_equal,
+ },
+ [T_CONS] { type: T_CONS, name: "cons", print: cons_print,
+ pointer: TRUE,
+ free: cons_free,
+ equal: cons_equal,
+ },
+};
+
+/** Number of entries in the types array. */
+static int type_sup = sizeof(types)/sizeof(types[0]);
+
+/** Get the type definition for a given type code.
+ *
+ * @param ty type code
+ * @return type definition or null
+ */
+SxprType *get_sxpr_type(int ty){
+ if(0 <= ty && ty < type_sup){
+ return types+ty;
+ }
+ return NULL;
+}
+
+/** The default print function.
+ *
+ * @param io stream to print to
+ * @param x sxpr to print
+ * @param flags print flags
+ * @return number of bytes written on success
+ */
+int default_print(IOStream *io, Sxpr x, unsigned flags){
+ return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x));
+}
+
+/** The default equal function.
+ * Uses eq().
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int default_equal(Sxpr x, Sxpr y){
+ return eq(x, y);
+}
+
+/** General sxpr print function.
+ * Prints an sxpr on a stream using the print function for the sxpr type.
+ * Printing is controlled by flags from the PrintFlags enum.
+ * If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr
+ * (for debugging).
+ *
+ * @param io stream to print to
+ * @param x sxpr to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int objprint(IOStream *io, Sxpr x, unsigned flags){
+ SxprType *def = get_sxpr_type(get_type(x));
+ ObjPrintFn *print_fn = (def && def->print ? def->print : default_print);
+ int k = 0;
+ if(!io) return k;
+ if(flags & PRINT_TYPE){
+ k += IOStream_print(io, "%s:", def->name);
+ }
+ k += print_fn(io, x, flags);
+ return k;
+}
+
+/** General sxpr free function.
+ * Frees an sxpr using the free function for its type.
+ * Free functions must recursively free any subsxprs.
+ * If no function is defined then the default is to
+ * free sxprs whose type has pointer true.
+ * Sxprs must not be used after freeing.
+ *
+ * @param x sxpr to free
+ */
+void objfree(Sxpr x){
+ SxprType *def = get_sxpr_type(get_type(x));
+
+ if(def){
+ if(def->free){
+ def->free(x);
+ } else if (def->pointer){
+ hfree(x);
+ }
+ }
+}
+
+/** General sxpr equality function.
+ * Compares x and y using the equal function for x.
+ * Uses default_equal() if x has no equal function.
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int objequal(Sxpr x, Sxpr y){
+ SxprType *def = get_sxpr_type(get_type(x));
+ ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal);
+ return equal_fn(x, y);
+}
+
+/** Search for a key in an alist.
+ * An alist is a list of conses, where the cars
+ * of the conses are the keys. Compares keys using equality.
+ *
+ * @param k key
+ * @param l alist to search
+ * @return first element of l with car k, or ONULL
+ */
+Sxpr assoc(Sxpr k, Sxpr l){
+ for( ; CONSP(l) ; l = CDR(l)){
+ Sxpr x = CAR(l);
+ if(CONSP(x) && objequal(k, CAR(x))){
+ return x;
+ }
+ }
+ return ONULL;
+}
+
+/** Search for a key in an alist.
+ * An alist is a list of conses, where the cars
+ * of the conses are the keys. Compares keys using eq.
+ *
+ * @param k key
+ * @param l alist to search
+ * @return first element of l with car k, or ONULL
+ */
+Sxpr assocq(Sxpr k, Sxpr l){
+ for( ; CONSP(l); l = CDR(l)){
+ Sxpr x = CAR(l);
+ if(CONSP(x) && eq(k, CAR(x))){
+ return x;
+ }
+ }
+ return ONULL;
+}
+
+/** Add a new key and value to an alist.
+ *
+ * @param k key
+ * @param l value
+ * @param l alist
+ * @return l with the new cell added to the front
+ */
+Sxpr acons(Sxpr k, Sxpr v, Sxpr l){
+ Sxpr x, y;
+ x = cons_new(k, v);
+ if(NOMEMP(x)) return x;
+ y = cons_new(x, l);
+ if(NOMEMP(y)) cons_free_cells(x);
+ return y;
+}
+
+/** Test if a list contains an element.
+ * Uses sxpr equality.
+ *
+ * @param l list
+ * @param x element to look for
+ * @return a tail of l with x as car, or ONULL
+ */
+Sxpr cons_member(Sxpr l, Sxpr x){
+ for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){}
+ return l;
+}
+
+/** Test if a list contains an element satisfying a test.
+ * The test function is called with v and an element of the list.
+ *
+ * @param l list
+ * @param test_fn test function to use
+ * @param v value for first argument to the test
+ * @return a tail of l with car satisfying the test, or 0
+ */
+Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
+ for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ }
+ return l;
+}
+
+/** Test if the elements of list 't' are a subset of the elements
+ * of list 's'. Element order is not significant.
+ *
+ * @param s element list to check subset of
+ * @param t element list to check if is a subset
+ * @return 1 if is a subset, 0 otherwise
+ */
+int cons_subset(Sxpr s, Sxpr t){
+ for( ; CONSP(t); t = CDR(t)){
+ if(!CONSP(cons_member(s, CAR(t)))){
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/** Test if two lists have equal sets of elements.
+ * Element order is not significant.
+ *
+ * @param s list to check
+ * @param t list to check
+ * @return 1 if equal, 0 otherwise
+ */
+int cons_set_equal(Sxpr s, Sxpr t){
+ return cons_subset(s, t) && cons_subset(t, s);
+}
+
+#ifdef USE_GC
+/*============================================================================*/
+/* The functions inside this ifdef are only safe if GC is used.
+ * Otherwise they may leak memory.
+ */
+
+/** Remove an element from a list (GC only).
+ * Uses sxpr equality and removes all instances, even
+ * if there are more than one.
+ *
+ * @param l list to remove elements from
+ * @param x element to remove
+ * @return modified input list
+ */
+Sxpr cons_remove(Sxpr l, Sxpr x){
+ return cons_remove_if(l, eq, x);
+}
+
+/** Remove elements satisfying a test (GC only).
+ * The test function is called with v and an element of the set.
+ *
+ * @param l list to remove elements from
+ * @param test_fn function to use to decide if an element should be removed
+ * @return modified input list
+ */
+Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
+ Sxpr prev = ONULL, elt, next;
+
+ for(elt = l; CONSP(elt); elt = next){
+ next = CDR(elt);
+ if(test_fn(v, CAR(elt))){
+ if(NULLP(prev)){
+ l = next;
+ } else {
+ CDR(prev) = next;
+ }
+ }
+ }
+ return l;
+}
+
+/** Set the value for a key in an alist (GC only).
+ * If the key is present, changes the value, otherwise
+ * adds a new cell.
+ *
+ * @param k key
+ * @param v value
+ * @param l alist
+ * @return modified or extended list
+ */
+Sxpr setf(Sxpr k, Sxpr v, Sxpr l){
+ Sxpr e = assoc(k, l);
+ if(NULLP(e)){
+ l = acons(k, v, l);
+ } else {
+ CAR(CDR(e)) = v;
+ }
+ return l;
+}
+/*============================================================================*/
+#endif /* USE_GC */
+
+/** Create a new atom with the given name.
+ *
+ * @param name the name
+ * @return new atom
+ */
+Sxpr atom_new(char *name){
+ Sxpr n, obj = ONOMEM;
+
+ n = string_new(name);
+ if(NOMEMP(n)) goto exit;
+ obj = HALLOC(ObjAtom, T_ATOM);
+ if(NOMEMP(obj)) goto exit;
+ OBJ_ATOM(obj)->name = n;
+ exit:
+ return obj;
+}
+
+/** Free an atom.
+ *
+ * @param obj to free
+ */
+void atom_free(Sxpr obj){
+ // Interned atoms are shared, so do not free.
+ if(OBJ_ATOM(obj)->interned) return;
+ objfree(OBJ_ATOM(obj)->name);
+ hfree(obj);
+}
+
+/** Print an atom. Prints the atom name.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes printed
+ */
+int atom_print(IOStream *io, Sxpr obj, unsigned flags){
+ //return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW));
+ return string_print(io, OBJ_ATOM(obj)->name, flags);
+}
+
+/** Atom equality.
+ *
+ * @param x to compare
+ * @param y to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int atom_equal(Sxpr x, Sxpr y){
+ int ok;
+ ok = eq(x, y);
+ if(ok) goto exit;
+ ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name);
+ if(ok) goto exit;
+ ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y);
+ exit:
+ return ok;
+}
+
+/** Get the name of an atom.
+ *
+ * @param obj atom
+ * @return name
+ */
+char * atom_name(Sxpr obj){
+ return string_string(OBJ_ATOM(obj)->name);
+}
+
+/** Get the C string from a string sxpr.
+ *
+ * @param obj string sxpr
+ * @return string
+ */
+char * string_string(Sxpr obj){
+ return OBJ_STRING(obj);
+}
+
+/** Get the length of a string.
+ *
+ * @param obj string
+ * @return length
+ */
+int string_length(Sxpr obj){
+ return strlen(OBJ_STRING(obj));
+}
+
+/** Create a new string. The input string is copied,
+ * and must be null-terminated.
+ *
+ * @param s characters to put in the string
+ * @return new sxpr
+ */
+Sxpr string_new(char *s){
+ int n = (s ? strlen(s) : 0);
+ Sxpr obj;
+ obj = halloc(n+1, T_STRING);
+ if(!NOMEMP(obj)){
+ char *str = OBJ_STRING(obj);
+ strncpy(str, s, n);
+ str[n] = '\0';
+ }
+ return obj;
+}
+
+/** Free a string.
+ *
+ * @param obj to free
+ */
+void string_free(Sxpr obj){
+ hfree(obj);
+}
+
+/** Determine if a string needs escapes when printed
+ * using the given flags.
+ *
+ * @param str string to check
+ * @param flags print flags
+ * @return 1 if needs escapes, 0 otherwise
+ */
+int needs_escapes(char *str, unsigned flags){
+ char *c;
+ int val = 0;
+
+ if(str){
+ for(c=str; *c; c++){
+ if(in_alpha_class(*c)) continue;
+ if(in_decimal_digit_class(*c)) continue;
+ if(in_class(*c, "/._+:@~-")) continue;
+ val = 1;
+ break;
+ }
+ }
+ //printf("\n> val=%d str=|%s|\n", val, str);
+ return val;
+}
+
+/** Print a string to a stream, with escapes if necessary.
+ *
+ * @param io stream to print to
+ * @param str string
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int _string_print(IOStream *io, char *str, unsigned flags){
+ int k = 0;
+ if((flags & PRINT_RAW) || !needs_escapes(str, flags)){
+ k += IOStream_print(io, str);
+ } else {
+ k += IOStream_print(io, "\"");
+ if(str){
+ char *s;
+ for(s = str; *s; s++){
+ if(*s < ' ' || *s >= 127 ){
+ switch(*s){
+ case '\a': k += IOStream_print(io, "\\a"); break;
+ case '\b': k += IOStream_print(io, "\\b"); break;
+ case '\f': k += IOStream_print(io, "\\f"); break;
+ case '\n': k += IOStream_print(io, "\\n"); break;
+ case '\r': k += IOStream_print(io, "\\r"); break;
+ case '\t': k += IOStream_print(io, "\\t"); break;
+ case '\v': k += IOStream_print(io, "\\v"); break;
+ default:
+ // Octal escape;
+ k += IOStream_print(io, "\\%o", *s);
+ break;
+ }
+ } else if(*s == c_double_quote ||
+ *s == c_single_quote ||
+ *s == c_escape){
+ k += IOStream_print(io, "\\%c", *s);
+ } else {
+ k+= IOStream_print(io, "%c", *s);
+ }
+ }
+ }
+ k += IOStream_print(io, "\"");
+ }
+ return k;
+}
+
+/** Print a string to a stream, with escapes if necessary.
+ *
+ * @param io stream to print to
+ * @param obj string
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int string_print(IOStream *io, Sxpr obj, unsigned flags){
+ return _string_print(io, OBJ_STRING(obj), flags);
+}
+
+/** Compare an sxpr with a string for equality.
+ *
+ * @param x string to compare with
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int string_equal(Sxpr x, Sxpr y){
+ int ok = 0;
+ ok = eq(x,y);
+ if(ok) goto exit;
+ ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y));
+ if(ok) goto exit;
+ ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y));
+ exit:
+ return ok;
+}
+
+/** Create a new cons cell.
+ * The cell is ONOMEM if either argument is.
+ *
+ * @param car sxpr for the car
+ * @param cdr sxpr for the cdr
+ * @return new cons
+ */
+Sxpr cons_new(Sxpr car, Sxpr cdr){
+ Sxpr obj;
+ if(NOMEMP(car) || NOMEMP(cdr)){
+ obj = ONOMEM;
+ } else {
+ obj = HALLOC(ObjCons, T_CONS);
+ if(!NOMEMP(obj)){
+ ObjCons *z = OBJ_CONS(obj);
+ z->car = car;
+ z->cdr = cdr;
+ }
+ }
+ return obj;
+}
+
+/** Push a new element onto a list.
+ *
+ * @param list list to add to
+ * @param elt element to add
+ * @return 0 if successful, error code otherwise
+ */
+int cons_push(Sxpr *list, Sxpr elt){
+ Sxpr l;
+ l = cons_new(elt, *list);
+ if(NOMEMP(l)) return -ENOMEM;
+ *list = l;
+ return 0;
+}
+
+/** Free a cons. Recursively frees the car and cdr.
+ *
+ * @param obj to free
+ */
+void cons_free(Sxpr obj){
+ Sxpr next;
+ for(; CONSP(obj); obj = next){
+ next = CDR(obj);
+ objfree(CAR(obj));
+ hfree(obj);
+ }
+ if(!NULLP(obj)){
+ objfree(obj);
+ }
+}
+
+/** Free a cons and its cdr cells, but not the car sxprs.
+ * Does nothing if called on something that is not a cons.
+ *
+ * @param obj to free
+ */
+void cons_free_cells(Sxpr obj){
+ Sxpr next;
+ for(; CONSP(obj); obj = next){
+ next = CDR(obj);
+ hfree(obj);
+ }
+}
+
+/** Print a cons.
+ * Prints the cons in list format if the cdrs are conses.
+ * uses pair (dot) format if the last cdr is not a cons (or null).
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int cons_print(IOStream *io, Sxpr obj, unsigned flags){
+ int first = 1;
+ int k = 0;
+ k += IOStream_print(io, "(");
+ for( ; CONSP(obj) ; obj = CDR(obj)){
+ if(first){
+ first = 0;
+ } else {
+ k += IOStream_print(io, " ");
+ }
+ k += objprint(io, CAR(obj), flags);
+ }
+ if(!NULLP(obj)){
+ k += IOStream_print(io, " . ");
+ k += objprint(io, obj, flags);
+ }
+ k += IOStream_print(io, ")");
+ return (IOStream_error(io) ? -1 : k);
+}
+
+/** Compare a cons with another sxpr for equality.
+ * If y is a cons, compares the cars and cdrs recursively.
+ *
+ * @param x cons to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int cons_equal(Sxpr x, Sxpr y){
+ return CONSP(y) &&
+ objequal(CAR(x), CAR(y)) &&
+ objequal(CDR(x), CDR(y));
+}
+
+/** Return the length of a cons list.
+ *
+ * @param obj list
+ * @return length
+ */
+int cons_length(Sxpr obj){
+ int count = 0;
+ for( ; CONSP(obj); obj = CDR(obj)){
+ count++;
+ }
+ return count;
+}
+
+/** Destructively reverse a cons list in-place.
+ * If the argument is not a cons it is returned unchanged.
+ *
+ * @param l to reverse
+ * @return reversed list
+ */
+Sxpr nrev(Sxpr l){
+ if(CONSP(l)){
+ // Iterate down the cells in the list making the cdr of
+ // each cell point to the previous cell. The last cell
+ // is the head of the reversed list.
+ Sxpr prev = ONULL;
+ Sxpr cell = l;
+ Sxpr next;
+
+ while(1){
+ next = CDR(cell);
+ CDR(cell) = prev;
+ if(!CONSP(next)) break;
+ prev = cell;
+ cell = next;
+ }
+ l = cell;
+ }
+ return l;
+}
+
+/** Print the null sxpr.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int null_print(IOStream *io, Sxpr obj, unsigned flags){
+ return IOStream_print(io, "()");
+}
+
+/** Print the `unspecified' sxpr none.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int none_print(IOStream *io, Sxpr obj, unsigned flags){
+ return IOStream_print(io, "<none>");
+}
+
+/** Print an integer.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int int_print(IOStream *io, Sxpr obj, unsigned flags){
+ return IOStream_print(io, "%d", OBJ_INT(obj));
+}
+
+/** Print a boolean.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int bool_print(IOStream *io, Sxpr obj, unsigned flags){
+ return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false));
+}
+
+int sxprp(Sxpr obj, Sxpr name){
+ return CONSP(obj) && objequal(CAR(obj), name);
+}
+
+/** Get the name of an element.
+ *
+ * @param obj element
+ * @return name
+ */
+Sxpr sxpr_name(Sxpr obj){
+ Sxpr val = ONONE;
+ if(CONSP(obj)){
+ val = CAR(obj);
+ } else if(STRINGP(obj) || ATOMP(obj)){
+ val = obj;
+ }
+ return val;
+}
+
+int sxpr_is(Sxpr obj, char *s){
+ if(ATOMP(obj)) return !strcmp(atom_name(obj), s);
+ if(STRINGP(obj)) return !strcmp(string_string(obj), s);
+ return 0;
+}
+
+int sxpr_elementp(Sxpr obj, Sxpr name){
+ int ok = 0;
+ ok = CONSP(obj) && objequal(CAR(obj), name);
+ return ok;
+}
+
+/** Get the attributes of an sxpr.
+ *
+ * @param obj sxpr
+ * @return attributes
+ */
+Sxpr sxpr_attributes(Sxpr obj){
+ Sxpr val = ONULL;
+ if(CONSP(obj)){
+ obj = CDR(obj);
+ if(CONSP(obj)){
+ obj = CAR(obj);
+ if(sxprp(obj, intern("@"))){
+ val = CDR(obj);
+ }
+ }
+ }
+ return val;
+}
+
+Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){
+ Sxpr val = ONONE;
+ val = assoc(sxpr_attributes(obj), key);
+ if(CONSP(val) && CONSP(CDR(val))){
+ val = CADR(def);
+ } else {
+ val = def;
+ }
+ return val;
+}
+
+/** Get the children of an sxpr.
+ *
+ * @param obj sxpr
+ * @return children
+ */
+Sxpr sxpr_children(Sxpr obj){
+ Sxpr val = ONULL;
+ if(CONSP(obj)){
+ val = CDR(obj);
+ if(CONSP(val) && sxprp(CAR(val), intern("@"))){
+ val = CDR(val);
+ }
+ }
+ return val;
+}
+
+Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){
+ Sxpr val = ONONE;
+ Sxpr l;
+ for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){
+ if(sxprp(CAR(l), name)){
+ val = CAR(l);
+ break;
+ }
+ }
+ if(NONEP(val)) val = def;
+ return val;
+}
+
+Sxpr sxpr_child0(Sxpr obj, Sxpr def){
+ Sxpr val = ONONE;
+ Sxpr l = sxpr_children(obj);
+ if(CONSP(l)){
+ val = CAR(l);
+ } else {
+ val = def;
+ }
+ return val;
+}
+
+Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def){
+ Sxpr val = def;
+ Sxpr l;
+ int i;
+ for (i = 0, l = sxpr_children(obj); CONSP(l); i++, l = CDR(l)){
+ if(i == n){
+ val = CAR(l);
+ break;
+ }
+ }
+ return val;
+}
+
+Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){
+ Sxpr val = ONONE;
+ val = sxpr_child(obj, name, ONONE);
+ if(NONEP(val)){
+ val = def;
+ } else {
+ val = sxpr_child0(val, def);
+ }
+ return val;
+}
+
+/** Table of interned symbols. Indexed by symbol name. */
+static HashTable *symbols = NULL;
+
+/** Hash function for entries in the symbol table.
+ *
+ * @param key to hash
+ * @return hashcode
+ */
+static Hashcode sym_hash_fn(void *key){
+ return hash_string((char*)key);
+}
+
+/** Key equality function for the symbol table.
+ *
+ * @param x to compare
+ * @param y to compare
+ * @return 1 if equal, 0 otherwise
+ */
+static int sym_equal_fn(void *x, void *y){
+ return !strcmp((char*)x, (char*)y);
+}
+
+/** Entry free function for the symbol table.
+ *
+ * @param table the entry is in
+ * @param entry being freed
+ */
+static void sym_free_fn(HashTable *table, HTEntry *entry){
+ if(entry){
+ objfree(((ObjAtom*)entry->value)->name);
+ HTEntry_free(entry);
+ }
+}
+
+/** Initialize the symbol table.
+ *
+ * @return 0 on sucess, error code otherwise
+ */
+static int init_symbols(void){
+ symbols = HashTable_new(100);
+ if(symbols){
+ symbols->key_hash_fn = sym_hash_fn;
+ symbols->key_equal_fn = sym_equal_fn;
+ symbols->entry_free_fn = sym_free_fn;
+ return 0;
+ }
+ return -1;
+}
+
+/** Cleanup the symbol table. Frees the table and all its symbols.
+ */
+void cleanup_symbols(void){
+ HashTable_free(symbols);
+ symbols = NULL;
+}
+
+/** Get the interned symbol with the given name.
+ * No new symbol is created.
+ *
+ * @return symbol or null
+ */
+Sxpr get_symbol(char *sym){
+ HTEntry *entry;
+ if(!symbols){
+ if(init_symbols()) return ONOMEM;
+ return ONULL;
+ }
+ entry = HashTable_get_entry(symbols, sym);
+ if(entry){
+ return OBJP(T_ATOM, entry->value);
+ } else {
+ return ONULL;
+ }
+}
+
+/** Get the interned symbol with the given name.
+ * Creates a new symbol if necessary.
+ *
+ * @return symbol
+ */
+Sxpr intern(char *sym){
+ Sxpr symbol = get_symbol(sym);
+ if(NULLP(symbol)){
+ if(!symbols) return ONOMEM;
+ symbol = atom_new(sym);
+ if(!NOMEMP(symbol)){
+ OBJ_ATOM(symbol)->interned = TRUE;
+ HashTable_add(symbols, atom_name(symbol), get_ptr(symbol));
+ }
+ }
+ return symbol;
+}
--- /dev/null
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _XUTIL_SXPR_H_
+#define _XUTIL_SXPR_H_
+
+#include <stdint.h>
+
+#include "hash_table.h"
+#include "iostream.h"
+#include "allocate.h"
+
+/** @file
+ * Definitions for rules and sxprs.
+ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/** Sxpr type. */
+typedef int16_t TypeCode;
+
+/** A typed sxpr handle.*/
+typedef struct Sxpr {
+ /** Sxpr type. */
+ TypeCode type;
+ union {
+ /** Sxpr value. */
+ unsigned long ul;
+ /** Pointer. */
+ void *ptr;
+ } v;
+} Sxpr;
+
+/** Sxpr type to indicate out of memory. */
+#define T_NOMEM ((TypeCode)-1)
+/** The 'unspecified' sxpr. */
+#define T_NONE ((TypeCode)0)
+/** The empty list. */
+#define T_NULL ((TypeCode)1)
+/** Unsigned integer. */
+#define T_UINT ((TypeCode)2)
+/** A string. */
+#define T_STRING ((TypeCode)3)
+/** An atom. */
+#define T_ATOM ((TypeCode)4)
+/** A boolean. */
+#define T_BOOL ((TypeCode)5)
+
+/** A cons (pair or list). */
+#define T_CONS ((TypeCode)10)
+
+/** An error. */
+#define T_ERR ((TypeCode)40)
+
+/** An atom. */
+typedef struct ObjAtom {
+ Sxpr name;
+ Hashcode hashcode;
+ int interned;
+} ObjAtom;
+
+/** A cons (pair). */
+typedef struct ObjCons {
+ Sxpr car;
+ Sxpr cdr;
+} ObjCons;
+
+/** A vector. */
+typedef struct ObjVector {
+ int n;
+ Sxpr data[0];
+} ObjVector;
+
+/** Flags for sxpr printing. */
+enum PrintFlags {
+ PRINT_RAW = 0x001,
+ PRINT_TYPE = 0x002,
+ PRINT_PRETTY = 0x004,
+ PRINT_NUM = 0x008,
+};
+
+/** An integer sxpr.
+ *
+ * @param ty type
+ * @param val integer value
+ */
+#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }}
+
+/** A pointer sxpr.
+ * If the pointer is non-null, returns an sxpr containing it.
+ * If the pointer is null, returns ONOMEM.
+ *
+ * @param ty type
+ * @param val pointer
+ */
+#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM)
+
+/** Make an integer sxpr containing a pointer.
+ *
+ * @param val pointer
+ */
+#define PTR(val) OBJP(T_UINT, (void*)(val))
+
+/** Make an integer sxpr.
+ * @param x value
+ */
+#define OINT(x) OBJI(T_UINT, x)
+
+/** Make an error sxpr.
+ *
+ * @param x value
+ */
+#define OERR(x) OBJI(T_ERR, x)
+
+/** Out of memory constant. */
+#define ONOMEM OBJI(T_NOMEM, 0)
+
+/** The `unspecified' constant. */
+#define ONONE OBJI(T_NONE, 0)
+
+/** Empty list constant. */
+#define ONULL OBJI(T_NULL, 0)
+
+/** False constant. */
+#define OFALSE OBJI(T_BOOL, 0)
+
+/** True constant. */
+#define OTRUE OBJI(T_BOOL, 1)
+
+/* Recognizers for the various sxpr types. */
+#define ATOMP(obj) has_type(obj, T_ATOM)
+#define BOOLP(obj) has_type(obj, T_BOOL)
+#define CONSP(obj) has_type(obj, T_CONS)
+#define ERRP(obj) has_type(obj, T_ERR)
+#define INTP(obj) has_type(obj, T_UINT)
+#define NOMEMP(obj) has_type(obj, T_NOMEM)
+#define NONEP(obj) has_type(obj, T_NONE)
+#define NULLP(obj) has_type(obj, T_NULL)
+#define STRINGP(obj) has_type(obj, T_STRING)
+
+#define TRUEP(obj) get_ul(obj)
+
+/** Convert an sxpr to an unsigned integer. */
+#define OBJ_UINT(x) get_ul(x)
+/** Convert an sxpr to an integer. */
+#define OBJ_INT(x) (int)get_ul(x)
+
+/* Conversions of sxprs to their values.
+ * No checking is done.
+ */
+#define OBJ_STRING(x) ((char*)get_ptr(x))
+#define OBJ_CONS(x) ((ObjCons*)get_ptr(x))
+#define OBJ_ATOM(x) ((ObjAtom*)get_ptr(x))
+#define OBJ_SET(x) ((ObjSet*)get_ptr(x))
+#define CAR(x) (OBJ_CONS(x)->car)
+#define CDR(x) (OBJ_CONS(x)->cdr)
+
+#define CAAR(x) (CAR(CAR(x)))
+#define CADR(x) (CAR(CDR(x)))
+#define CDAR(x) (CDR(CAR(x)))
+#define CDDR(x) (CDR(CDR(x)))
+
+/** Get the integer value from an sxpr.
+ *
+ * @param obj sxpr
+ * @return value
+ */
+static inline unsigned long get_ul(Sxpr obj){
+ return obj.v.ul;
+}
+
+/** Get the pointer value from an sxpr.
+ *
+ * @param obj sxpr
+ * @return value
+ */
+static inline void * get_ptr(Sxpr obj){
+ return obj.v.ptr;
+}
+
+/** Create an sxpr containing a pointer.
+ *
+ * @param type typecode
+ * @param val pointer
+ * @return sxpr
+ */
+static inline Sxpr obj_ptr(TypeCode type, void *val){
+ return (Sxpr){ type: type, v: { ptr: val } };
+}
+
+/** Create an sxpr containing an integer.
+ *
+ * @param type typecode
+ * @param val integer
+ * @return sxpr
+ */
+static inline Sxpr obj_ul(TypeCode type, unsigned long val){
+ return (Sxpr){ type: type, v: { ul: val } };
+}
+
+/** Get the type of an sxpr.
+ *
+ * @param obj sxpr
+ * @return type
+ */
+static inline TypeCode get_type(Sxpr obj){
+ return obj.type;
+}
+
+/** Check the type of an sxpr.
+ *
+ * @param obj sxpr
+ * @param type to check
+ * @return 1 if has the type, 0 otherwise
+ */
+static inline int has_type(Sxpr obj, TypeCode type){
+ return get_type(obj) == type;
+}
+
+/** Compare sxprs for literal equality of type and value.
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+static inline int eq(Sxpr x, Sxpr y){
+ return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y)));
+}
+
+/** Checked version of CAR
+ *
+ * @param x sxpr
+ * @return CAR if a cons, x otherwise
+ */
+static inline Sxpr car(Sxpr x){
+ return (CONSP(x) ? CAR(x) : x);
+}
+
+/** Checked version of CDR.
+ *
+ * @param x sxpr
+ * @return CDR if a cons, null otherwise
+ */
+static inline Sxpr cdr(Sxpr x){
+ return (CONSP(x) ? CDR(x) : ONULL);
+}
+
+/** Allocate some memory and return an sxpr containing it.
+ * Returns ONOMEM if allocation failed.
+ *
+ * @param n number of bytes to allocate
+ * @param ty typecode
+ * @return sxpr
+ */
+static inline Sxpr halloc(size_t n, TypeCode ty){
+ return OBJP(ty, allocate(n));
+}
+
+/** Allocate an sxpr containing a pointer to the given type.
+ *
+ * @param ty type (uses sizeof to determine how many bytes to allocate)
+ * @param code typecode
+ * @return sxpr, ONOMEM if allocation failed
+ */
+#define HALLOC(ty, code) halloc(sizeof(ty), code)
+
+typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags);
+typedef int ObjEqualFn(Sxpr obj, Sxpr other);
+typedef void ObjFreeFn(Sxpr obj);
+
+/** An sxpr type definition. */
+typedef struct SxprType {
+ TypeCode type;
+ char *name;
+ int pointer;
+ ObjPrintFn *print;
+ ObjEqualFn *equal;
+ ObjFreeFn *free;
+} SxprType;
+
+
+extern SxprType *get_sxpr_type(int ty);
+
+/** Free the pointer in an sxpr.
+ *
+ * @param x sxpr containing a pointer
+ */
+static inline void hfree(Sxpr x){
+ deallocate(get_ptr(x));
+}
+
+extern int objprint(IOStream *io, Sxpr x, unsigned flags);
+extern int objequal(Sxpr x, Sxpr y);
+extern void objfree(Sxpr x);
+
+extern void cons_free_cells(Sxpr obj);
+extern Sxpr intern(char *s);
+
+extern Sxpr assoc(Sxpr k, Sxpr l);
+extern Sxpr assocq(Sxpr k, Sxpr l);
+extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l);
+extern Sxpr nrev(Sxpr l);
+extern Sxpr cons_member(Sxpr l, Sxpr x);
+extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
+extern int cons_subset(Sxpr s, Sxpr t);
+extern int cons_set_equal(Sxpr s, Sxpr t);
+
+#ifdef USE_GC
+extern Sxpr cons_remove(Sxpr l, Sxpr x);
+extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
+#endif
+
+extern Sxpr atom_new(char *name);
+extern char * atom_name(Sxpr obj);
+
+extern Sxpr string_new(char *s);
+extern char * string_string(Sxpr obj);
+extern int string_length(Sxpr obj);
+
+extern Sxpr cons_new(Sxpr car, Sxpr cdr);
+extern int cons_push(Sxpr *list, Sxpr elt);
+extern int cons_length(Sxpr obj);
+
+Sxpr sxpr_name(Sxpr obj);
+int sxpr_is(Sxpr obj, char *s);
+int sxpr_elementp(Sxpr obj, Sxpr name);
+Sxpr sxpr_attributes(Sxpr obj);
+Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def);
+Sxpr sxpr_children(Sxpr obj);
+Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def);
+Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def);
+Sxpr sxpr_child0(Sxpr obj, Sxpr def);
+Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def);
+
+/** Create a new atom.
+ *
+ * @param s atom name
+ * @return new atom
+ */
+static inline Sxpr mkatom(char *s){
+ return atom_new(s);
+}
+
+/** Create a new string sxpr.
+ *
+ * @param s string bytes (copied)
+ * @return new string
+ */
+static inline Sxpr mkstring(char *s){
+ return string_new(s);
+}
+
+/** Create an integer sxpr.
+ *
+ * @param i value
+ * @return sxpr
+ */
+static inline Sxpr mkint(int i){
+ return OBJI(T_UINT, i);
+}
+
+/** Create a boolean sxpr.
+ *
+ * @param b value
+ * @return sxpr
+ */
+static inline Sxpr mkbool(int b){
+ return OBJI(T_BOOL, (b ? 1 : 0));
+}
+
+/* Constants used in parsing and printing. */
+#define k_list_open "("
+#define c_list_open '('
+#define k_list_close ")"
+#define c_list_close ')'
+#define k_true "true"
+#define k_false "false"
+
+#define c_var '$'
+#define c_escape '\\'
+#define c_single_quote '\''
+#define c_double_quote '"'
+#define c_string_open c_double_quote
+#define c_string_close c_double_quote
+#define c_data_open '['
+#define c_data_close ']'
+#define c_binary '*'
+#define c_eval '!'
+#define c_concat_open '{'
+#define c_concat_close '}'
+
+#endif /* ! _XUTIL_SXPR_H_ */
--- /dev/null
+#include <errno.h>
+#include "xdr.h"
+
+#define MODULE_NAME "XDR"
+//#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** @file
+ * XDR packer/unpacker for elements.
+ *
+ * string -> [T_STRING] [len:u16] <len bytes>
+ * atom -> [T_ATOM] [len:u16] <len bytes>
+ * uint -> [T_UINT] [value]
+ * cons -> [T_LIST] {1 elt}* 0
+ * null -> [T_NULL]
+ * none -> [T_NONE]
+ * bool -> [T_BOOL] { 0:u8 | 1:u8 }
+ *
+ * types packed as u16.
+ *
+ * So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL]
+ * () -> [T_NULL]
+ */
+
+int pack_bool(IOStream *io, int x){
+ int err=0;
+ //dprintf("> x=%d\n", x);
+ err = IOStream_print(io, "%c", 0xff & x);
+ if(err > 0) err = 0;
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unpack_bool(IOStream *io, int *x){
+ int err = 0;
+ int c;
+ //dprintf(">\n");
+ c = IOStream_getc(io);
+ *x = (c < 0 ? 0 : c);
+ err = IOStream_error(io);
+ if(c < 0 && !err) err = -EIO;
+ //dprintf("< err=%d x=%d\n", err, *x);
+ return err;
+}
+
+int pack_ushort(IOStream *io, unsigned short x){
+ int err=0;
+ //dprintf("> x=%u\n", x);
+ err = IOStream_print(io, "%c%c",
+ 0xff & (x >> 8),
+ 0xff & (x ));
+ if(err > 0) err = 0;
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unpack_ushort(IOStream *io, unsigned short *x){
+ int err = 0;
+ int i, c = 0;
+ //dprintf(">\n");
+ *x = 0;
+ for(i = 0; i< 2; i++){
+ c = IOStream_getc(io);
+ if(c < 0) break;
+ *x <<= 8;
+ *x |= (0xff & c);
+ }
+ err = IOStream_error(io);
+
+ if(c < 0 && !err) err = -EIO;
+ //dprintf("< err=%d x=%u\n", err, *x);
+ return err;
+}
+
+int pack_type(IOStream *io, unsigned short x){
+ return pack_ushort(io, x);
+}
+
+int unpack_type(IOStream *io, unsigned short *x){
+ return unpack_ushort(io, x);
+}
+
+int pack_uint(IOStream *io, unsigned int x){
+ int err=0;
+ //dprintf("> x=%u\n", x);
+ err = IOStream_print(io, "%c%c%c%c",
+ 0xff & (x >> 24),
+ 0xff & (x >> 16),
+ 0xff & (x >> 8),
+ 0xff & (x ));
+ if(err > 0) err = 0;
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unpack_uint(IOStream *io, unsigned int *x){
+ int err = 0;
+ int i, c = 0;
+ //dprintf(">\n");
+ *x = 0;
+ for(i = 0; i< 4; i++){
+ c = IOStream_getc(io);
+ if(c < 0) break;
+ *x <<= 8;
+ *x |= (0xff & c);
+ }
+ err = IOStream_error(io);
+ if(c < 0 && !err) err = -EIO;
+ //dprintf("< err=%d x=%u\n", err, *x);
+ return err;
+}
+
+int pack_string(IOStream *io, Sxpr x){
+ int err = 0;
+ unsigned short n = 0xffff & string_length(x);
+ char *s = string_string(x);
+ int i;
+ //dprintf("> n=%d s=%s\n", n, s);
+ err = pack_ushort(io, n);
+ if(err) goto exit;
+ for(i = 0; i < n; i++){
+ err = IOStream_print(io, "%c", s[i]);
+ if(err < 0) break;
+ }
+ if(err > 0) err = 0;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unpack_string(IOStream *io, Sxpr *x){
+ int err;
+ unsigned short n;
+ int i, c = 0;
+ char *s;
+ Sxpr val = ONONE;
+
+ //dprintf(">\n");
+ err = unpack_ushort(io, &n);
+ if(err) goto exit;
+ val = halloc(n+1, T_STRING);
+ if(NOMEMP(val)){
+ err = -ENOMEM;
+ goto exit;
+ }
+ s = string_string(val);
+ for(i=0; i<n; i++){
+ c = IOStream_getc(io);
+ if(c < 0) break;
+ s[i] = (char)c;
+ }
+ s[n] = '\0';
+ exit:
+ err = IOStream_error(io);
+ if(c < 0 && !err) err = -EIO;
+ if(err){
+ objfree(val);
+ val = ONONE;
+ }
+ *x = val;
+ //IOStream_print(iostdout, "n=%d str=", n);
+ //objprint(iostdout, *x, 0);
+ //IOStream_print(iostdout, "\n");
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int pack_cons(IOStream *io, Sxpr x){
+ int err = 0;
+ Sxpr l;
+ //dprintf(">\n");
+ for(l = x; CONSP(l); l = CDR(l)){
+ err = pack_bool(io, 1);
+ if(err) goto exit;
+ err = pack_sxpr(io, CAR(l));
+ if(err) goto exit;
+ }
+ err = pack_bool(io, 0);
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unpack_cons(IOStream *io, Sxpr *x){
+ int err = 0;
+ int more = 0;
+ Sxpr u = ONONE, v = ONONE, val = ONULL;
+
+ dprintf(">\n");
+ while(1){
+ err = unpack_bool(io, &more);
+ if(err) goto exit;
+ if(!more){
+ //IOStream_print(iostdout, "unpack_cons 1 val=");
+ ////objprint(iostdout, val, 0);
+ IOStream_print(iostdout, "\n");
+
+ val = nrev(val);
+
+ //IOStream_print(iostdout, "unpack_cons 2 val=");
+ //objprint(iostdout, val, 0);
+ //IOStream_print(iostdout, "\n");
+
+ break;
+ }
+ err = unpack_sxpr(io, &u);
+ if(err) goto exit;
+ v = cons_new(u, val);
+ if(NOMEMP(v)){
+ err = -ENOMEM;
+ objfree(u);
+ goto exit;
+ }
+ val = v;
+ }
+ exit:
+ if(err){
+ objfree(val);
+ val = ONONE;
+ }
+ *x = val;
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int pack_sxpr(IOStream *io, Sxpr x){
+ int err = 0;
+ unsigned short type = get_type(x);
+ //dprintf(">\n");
+ //objprint(iostdout, x, 0);
+ //IOStream_print(iostdout, "\n");
+
+ err = pack_type(io, type);
+ if(err) goto exit;
+ switch(type){
+ case T_NULL:
+ break;
+ case T_NONE:
+ break;
+ case T_BOOL:
+ err = pack_bool(io, get_ul(x));
+ break;
+ case T_CONS:
+ err = pack_cons(io, x);
+ break;
+ case T_ATOM:
+ err = pack_string(io, OBJ_ATOM(x)->name);
+ break;
+ case T_STRING:
+ err = pack_string(io, x);
+ break;
+ case T_UINT:
+ err = pack_uint(io, get_ul(x));
+ break;
+ default:
+ err = -EINVAL;
+ IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
+ break;
+ }
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unpack_sxpr(IOStream *io, Sxpr *x){
+ int err = 0;
+ unsigned short type;
+ unsigned int u;
+ Sxpr val = ONONE, y;
+
+ //dprintf(">\n");
+ err = unpack_type(io, &type);
+ if(err) goto exit;
+ switch(type){
+ case T_NULL:
+ val = ONULL;
+ break;
+ case T_NONE:
+ val = ONONE;
+ break;
+ case T_CONS:
+ err = unpack_cons(io, &val);
+ break;
+ case T_BOOL:
+ err = unpack_bool(io, &u);
+ if(err) goto exit;
+ val = (u ? OTRUE : OFALSE);
+ break;
+ case T_ATOM:
+ err = unpack_string(io, &y);
+ if(err) goto exit;
+ val = intern(string_string(y));
+ objfree(y);
+ break;
+ case T_STRING:
+ err = unpack_string(io, &val);
+ break;
+ case T_UINT:
+ err = unpack_uint(io, &u);
+ if(err) goto exit;
+ val = OBJI(type, u);
+ break;
+ default:
+ err = -EINVAL;
+ IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
+ break;
+ }
+ exit:
+ *x = (err ? ONONE : val);
+ //IOStream_print(iostdout, "sxpr=");
+ //objprint(iostdout, *x, 0);
+ //IOStream_print(iostdout, "\n");
+ //dprintf("< err=%d\n", err);
+ return err;
+}
--- /dev/null
+#ifndef _XUTIL_XDR_H_
+#define _XUTIL_XDR_H_
+#include "iostream.h"
+#include "sxpr.h"
+
+int pack_type(IOStream *io, unsigned short x);
+
+int unpack_type(IOStream *io, unsigned short *x);
+
+int pack_bool(IOStream *io, int x);
+
+int unpack_bool(IOStream *io, int *x);
+
+int pack_uint(IOStream *out, unsigned int x);
+
+int unpack_uint(IOStream *in, unsigned int *x);
+
+int pack_string(IOStream *out, Sxpr x);
+
+int unpack_string(IOStream *in, Sxpr *x);
+
+int pack_cons(IOStream *out, Sxpr x);
+
+int unpack_cons(IOStream *in, Sxpr *x);
+
+int pack_sxpr(IOStream *out, Sxpr x);
+
+int unpack_sxpr(IOStream *in, Sxpr *x);
+
+#endif /* _XUTIL_XDR_H_ */
--- /dev/null
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef _XEN_XFR_STUB_
+#include "dom0_defs.h"
+#include "mem_defs.h"
+#endif
+
+#include "xen_domain.h"
+#include "marshal.h"
+#include "xdr.h"
+
+#define MODULE_NAME "XFRD"
+#define DEBUG 1
+#include "debug.h"
+
+/** Write domain state.
+ *
+ * At some point during this the domain is suspended, and then there's no way back.
+ * Even if something later goes wrong we can't restart the domain.
+ */
+int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n){
+ int err = 0;
+ char buf[1024];
+ int n, k, d, buf_n;
+ dprintf("> dom=%d\n", dom);
+#ifdef _XEN_XFR_STUB_
+ err = marshal_uint32(io, dom);
+ if(err) goto exit;
+ err = marshal_string(io, vmconfig, vmconfig_n);
+ if(err) goto exit;
+ n = 32 * 1024 * 1024;
+ buf_n = sizeof(buf);
+ err = marshal_uint32(io, n);
+ for(k = 0; k < n; k += d){
+ d = n - k;
+ if(d > buf_n) d = buf_n;
+ err = marshal_bytes(io, buf, d);
+ if(err) goto exit;
+ //dprintf("> k=%d n=%d\n", k, n);
+ }
+
+ exit:
+#else
+#endif
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Receive domain state.
+ * Create a new domain and store the received state into it.
+ */
+int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n){
+ int err = 0;
+ char buf[1024];
+ int n, k, d, buf_n;
+ dprintf(">\n");
+#ifdef _XEN_XFR_STUB_
+ err = unmarshal_uint32(io, dom);
+ if(err) goto exit;
+ err = unmarshal_new_string(io, vmconfig, vmconfig_n);
+ if(err) goto exit;
+ err = unmarshal_uint32(io, &n);
+ buf_n = sizeof(buf);
+ for(k = 0; k < n; k += d){
+ d = n - k;
+ if(d > buf_n) d = buf_n;
+ err = unmarshal_bytes(io, buf, d);
+ if(err) goto exit;
+ //dprintf("> k=%d n=%d\n", k, n);
+ }
+ exit:
+#else
+#endif
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Configure a new domain. Talk to xend. Use libcurl?
+ */
+int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){
+ int err = 0;
+ dprintf(">\n");
+#ifdef _XEN_XFR_STUB_
+#else
+#endif
+ dprintf("< err=%d\n", err);
+ return err;
+}
--- /dev/null
+#ifndef _XFRD_XEN_DOMAIN_H_
+#define _XFRD_XEN_DOMAIN_H_
+#include <sys/types.h>
+#include <iostream.h>
+#include "connection.h"
+
+/** Define to use stubs. Undefine to use Xen ops. */
+//#define _XEN_XFR_STUB_
+
+extern int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n);
+extern int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n);
+
+
+extern int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n);
+#endif
--- /dev/null
+/** @file
+ * XFRD - Domain Transfer Daemon for Xen.
+ *
+ * The xfrd is forked by xend to transfer a vm to a remote system.
+ *
+ * The vm is suspended, then its state and memory are transferred to the remote system.
+ * The remote system attempts to create a vm and copy the transferred state and memory into it,
+ * finally resuming the vm. If all is OK the vm ends up running on the remote
+ * system and is removed from the originating system. If the transfer does not complete
+ * successfully the originating system attempts to resume the vm.
+ * The children exit when the transfer completes.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+
+#include "allocate.h"
+#include "file_stream.h"
+#include "string_stream.h"
+#include "lzi_stream.h"
+#include "sys_net.h"
+#include "sys_string.h"
+
+#include "xdr.h"
+#include "enum.h"
+#include "xfrd.h"
+
+#include "xen_domain.h"
+
+#include "connection.h"
+#include "select.h"
+
+#define MODULE_NAME "XFRD"
+#define DEBUG 1
+#include "debug.h"
+
+/*
+sender:
+ xend connects to xfrd and writes migrate message
+ xend writes domain config to xfrd
+
+ xfrd forks
+
+ xfrd connects to peer
+ xfrd sends hello, reads response
+ xfrd sends domain
+ xfrd reads response
+ reports progress/status to xend
+
+ xend reads xfrd for progress/status, disconnects
+ If ok, destroys domain.
+ If not ok, unpauses domain.
+
+receiver:
+ xfrd accepts connection on inbound port
+ xfrd forks and accepts connection
+ xfrd receives hello, writes response
+ xfrd receives domain
+ xfrd connects to xend, configures new domain
+ xfrd writes status back to peer, child exits
+
+
+ (xfr.hello <major> <minor>)
+ (xfr.err <code> <reason>)
+
+ xend->xfrd (xfr.migrate <domain> <vmconfig> <host> <port>)
+ (xfr.save <domain> <vmconfig> <file>)
+ xfrd->xend (xfr.suspend <domain>)
+ xfrd->xend (xfr.progress <percent> <rate: kb/s>)
+ xfrd->xend (xfr.err <code> <reason>) | (xfr.ok <domain>)
+ xfrd->xfrd (xfr.xfr <domain>)
+ xfrd->xfrd (xfr.err <code>) | (xfr.ok <domain>)
+
+ xfrd->xend (xfr.configure <domain> <vmconfig>)
+ */
+
+Sxpr oxfr_configure; // (xfr.configure <vmid> <vmconfig>)
+Sxpr oxfr_err; // (xfr.err <code>)
+Sxpr oxfr_hello; // (xfr.hello <major> <minor>)
+Sxpr oxfr_migrate; // (xfr.migrate <vmid> <vmconfig> <host> <port>)
+Sxpr oxfr_ok; // (xfr.ok <value>)
+Sxpr oxfr_progress; // (xfr.progress <percent> <rate: kb/s>)
+Sxpr oxfr_save; // (xfr.save <vmid> <vmconfig> <file>)
+Sxpr oxfr_suspend; // (xfr.suspend <vmid>)
+Sxpr oxfr_xfr; // (xfr.xfr <vmid>)
+
+void xfr_init(void){
+ oxfr_configure = intern("xfr.configure");
+ oxfr_err = intern("xfr.err");
+ oxfr_hello = intern("xfr.hello");
+ oxfr_migrate = intern("xfr.migrate");
+ oxfr_ok = intern("xfr.ok");
+ oxfr_progress = intern("xfr.progress");
+ oxfr_save = intern("xfr.save");
+ oxfr_suspend = intern("xfr.suspend");
+ oxfr_xfr = intern("xfr.xfr");
+}
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define PROGRAM "xfrd"
+
+#define OPT_PORT 'P'
+#define KEY_PORT "port"
+#define DOC_PORT "<port>\n\txfr port (as a number or service name)"
+
+#define OPT_COMPRESS 'Z'
+#define KEY_COMPRESS "compress"
+#define DOC_COMPRESS "\n\tuse compression for migration"
+
+#define OPT_HELP 'h'
+#define KEY_HELP "help"
+#define DOC_HELP "\n\tprint help"
+
+#define OPT_VERSION 'v'
+#define KEY_VERSION "version"
+#define DOC_VERSION "\n\tprint version"
+
+#define OPT_VERBOSE 'V'
+#define KEY_VERBOSE "verbose"
+#define DOC_VERBOSE "\n\tverbose flag"
+
+/** Print a usage message.
+ * Prints to stdout if err is zero, and exits with 0.
+ * Prints to stderr if err is non-zero, and exits with 1.
+ */
+void usage(int err){
+ FILE *out = (err ? stderr : stdout);
+
+ fprintf(out, "Usage: %s [options]\n", PROGRAM);
+ fprintf(out, "-%c, --%s %s\n", OPT_PORT, KEY_PORT, DOC_PORT);
+ fprintf(out, "-%c, --%s %s\n", OPT_COMPRESS, KEY_COMPRESS, DOC_COMPRESS);
+ fprintf(out, "-%c, --%s %s\n", OPT_VERBOSE, KEY_VERBOSE, DOC_VERBOSE);
+ fprintf(out, "-%c, --%s %s\n", OPT_VERSION, KEY_VERSION, DOC_VERSION);
+ fprintf(out, "-%c, --%s %s\n", OPT_HELP, KEY_HELP, DOC_HELP);
+ exit(err ? 1 : 0);
+}
+
+/** Short options. Options followed by ':' take an argument. */
+static char *short_opts = (char[]){
+ OPT_PORT, ':',
+ OPT_COMPRESS,
+ OPT_HELP,
+ OPT_VERSION,
+ OPT_VERBOSE,
+ 0 };
+
+/** Long options. */
+static struct option const long_opts[] = {
+ { KEY_PORT, required_argument, NULL, OPT_PORT },
+ { KEY_COMPRESS, no_argument, NULL, OPT_COMPRESS },
+ { KEY_HELP, no_argument, NULL, OPT_HELP },
+ { KEY_VERSION, no_argument, NULL, OPT_VERSION },
+ { KEY_VERBOSE, no_argument, NULL, OPT_VERBOSE },
+ { NULL, 0, NULL, 0 }
+};
+
+typedef struct Args {
+ int bufsize;
+ unsigned long port;
+ int verbose;
+ int compress;
+} Args;
+
+/** Transfer states. */
+enum {
+ XFR_INIT,
+ XFR_HELLO,
+ XFR_STATE,
+ XFR_RUN,
+ XFR_FAIL,
+ XFR_DONE,
+ XFR_MAX
+};
+
+/** Initialize an array element for a constant to its string name. */
+#define VALDEF(val) { val, #val }
+
+/** Names for the transfer states. */
+static EnumDef xfr_states[] = {
+ VALDEF(XFR_INIT),
+ VALDEF(XFR_HELLO),
+ VALDEF(XFR_STATE),
+ VALDEF(XFR_RUN),
+ VALDEF(XFR_FAIL),
+ VALDEF(XFR_DONE),
+ { 0, NULL }
+};
+
+
+/** State machine for transfer. */
+typedef struct XfrState {
+ /** Current state. */
+ int state;
+ /** Error codes for the states. */
+ int state_err[XFR_MAX];
+ /** First error. */
+ int err;
+ /** State when first error happened. */
+ int err_state;
+
+ uint32_t vmid;
+ char* vmconfig;
+ int vmconfig_n;
+ unsigned long xfr_port;
+ char *xfr_host;
+ uint32_t vmid_new;
+} XfrState;
+
+/** Get the name of a transfer state.
+ *
+ * @param s state
+ * @return name
+ */
+char * xfr_state_name(int s){
+ return enum_val_to_name(s, xfr_states);
+}
+
+/** Set the state of a transfer.
+ *
+ * @param s transfer
+ * @param state state
+ * @return state
+ */
+int XfrState_set_state(XfrState *s, int state){
+ s->state = state;
+ return s->state;
+}
+
+/** Get the state of a transfer.
+ *
+ * @param s transfer
+ * @return state
+ */
+int XfrState_get_state(XfrState *s){
+ return s->state;
+}
+
+/** Set an error in the current state.
+ * Does nothing if an error is already set.
+ *
+ * @param s transfer
+ * @param err error
+ * @return error
+ */
+int XfrState_set_err(XfrState *s, int err){
+ if(!s->state_err[s->state]){
+ s->state_err[s->state] = err;
+ }
+ if(!s->err){
+ s->err = err;
+ s->err_state = s->state;
+ }
+ return err;
+}
+
+/** Get the error in the current state.
+ *
+ * @param s transfer
+ * @return error
+ */
+int XfrState_get_err(XfrState *s){
+ return s->state_err[s->state];
+}
+
+/** Get the first error of a transfer.
+ *
+ * @param s transfer
+ * @return error
+ */
+int XfrState_first_err(XfrState *s){
+ return s->err;
+}
+
+/** Get the state a transfer was in when it had its first error.
+ *
+ * @param s transfer
+ * @return error state
+ */
+int XfrState_first_err_state(XfrState *s){
+ return s->err_state;
+}
+
+/** Xfrd arguments. */
+static Args _args = {};
+
+/** Xfrd arguments. */
+static Args *args = &_args;
+
+/** Set xfrd default arguments.
+ *
+ * @param args arguments to set
+ */
+void set_defaults(Args *args){
+ args->compress = FALSE;
+ args->bufsize = 128 * 1024;
+ args->port = htons(XFRD_PORT);
+}
+
+int stringof(Sxpr exp, char **s){
+ int err = 0;
+ dprintf(">\n");
+ objprint(iostdout, exp, PRINT_TYPE); IOStream_print(iostdout, "\n");
+ if(ATOMP(exp)){
+ *s = atom_name(exp);
+ } else if(STRINGP(exp)){
+ *s = string_string(exp);
+ } else {
+ err = -EINVAL;
+ *s = NULL;
+ }
+ dprintf("< err=%d s=%s\n", err, *s);
+ return err;
+}
+
+int intof(Sxpr exp, int *v){
+ int err = 0;
+ char *s;
+ unsigned long l;
+ dprintf(">\n");
+ objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+ if(INTP(exp)){
+ *v = OBJ_INT(exp);
+ } else {
+ err = stringof(exp, &s);
+ if(err) goto exit;
+ err = convert_atoul(s, &l);
+ *v = (int)l;
+ }
+ exit:
+ dprintf("< err=%d v=%d\n", err, *v);
+ return err;
+}
+
+int addrof(Sxpr exp, uint32_t *v){
+ char *h;
+ unsigned long a;
+ int err = 0;
+ dprintf(">\n");
+ objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+ err = stringof(exp, &h);
+ if(err) goto exit;
+ if(get_host_address(h, &a)){
+ *v = a;
+ } else {
+ err = -EINVAL;
+ }
+ exit:
+ dprintf("< err=%d v=%x\n", err, *v);
+ return err;
+}
+
+int portof(Sxpr exp, uint16_t *v){
+ char *s;
+ int err = 0;
+ dprintf(">\n");
+ objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+ if(INTP(exp)){
+ *v = get_ul(exp);
+ *v = htons(*v);
+ } else {
+ unsigned long p;
+ err = stringof(exp, &s);
+ if(err) goto exit;
+ if(get_service_port(s, &p)){
+ *v = p;
+ } else {
+ err = -EINVAL;
+ }
+ }
+ exit:
+ dprintf("< err=%d v=%u\n", err, *v);
+ return err;
+}
+
+static inline struct in_addr inaddr(uint32_t addr){
+ return (struct in_addr){ .s_addr = addr };
+}
+
+time_t stats(time_t t0, uint64_t offset, uint64_t memory, float *percent, float *rate){
+ time_t t1 = time(NULL);
+ *percent = (offset * 100.0f) / memory;
+ t1 = time(NULL) - t0;
+ *rate = (t1 ? offset/(t1 * 1024.0f) : 0.0f);
+ return t1;
+}
+
+/** Notify success or error.
+ *
+ * @param conn connection
+ * @param errcode error code
+ * @return 0 on success, error code otherwise
+ */
+int xfr_error(Conn *conn, int errcode){
+ int err = 0;
+ if(!conn->out) return -ENOTCONN;
+ err = pack_type(conn->out, T_CONS);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, oxfr_err);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, mkint(errcode));
+ if(err) goto exit;
+ err = pack_bool(conn->out, 0);
+ exit:
+ return err;
+}
+
+/** Read a response message - error or ok.
+ *
+ * @param conn connection
+ * @return 0 on success, error code otherwise
+ */
+int xfr_response(Conn *conn){
+ int err;
+ Sxpr sxpr;
+
+ dprintf(">\n");
+ if(!conn->out) return -ENOTCONN;
+ err = unpack_sxpr(conn->in, &sxpr);
+ if(err) goto exit;
+ if(sxpr_elementp(sxpr, oxfr_err)){
+ int errcode;
+ err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode);
+ if(err) goto exit;
+ err = errcode;
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Get the initial hello message and check the protocol version.
+ * It is an error to receive anything other than a hello message
+ * with the correct protocol version.
+ *
+ * @param conn connection
+ * @return 0 on success, error code otherwise
+ */
+int xfr_hello(Conn *conn){
+ int err;
+ uint32_t major = XFR_PROTO_MAJOR, minor = XFR_PROTO_MINOR;
+ uint32_t hello_major, hello_minor;
+ Sxpr sxpr;
+ if(!conn->in) return -ENOTCONN;
+ dprintf(">\n");
+ err = unpack_sxpr(conn->in, &sxpr);
+ if(err) goto exit;
+ if(!sxpr_elementp(sxpr, oxfr_hello)){
+ dprintf("> sxpr_elementp test failed\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ err = intof(sxpr_childN(sxpr, 0, ONONE), &hello_major);
+ if(err) goto exit;
+ err = intof(sxpr_childN(sxpr, 1, ONONE), &hello_minor);
+ if(err) goto exit;
+ if(hello_major != major || hello_minor != minor){
+ eprintf("> Wanted protocol version %d.%d, got %d.%d",
+ major, minor, hello_major, hello_minor);
+ err = -EINVAL;
+ goto exit;
+ }
+ exit:
+ xfr_error(conn, err);
+ if(err){
+ eprintf("> Hello failed: %d\n", err);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Send the initial hello message.
+ *
+ * @param conn connection
+ * @param msg message
+ * @return 0 on success, error code otherwise
+ */
+int xfr_send_hello(Conn *conn){
+ int err = 0;
+ dprintf(">\n");
+ err = pack_type(conn->out, T_CONS);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, oxfr_hello);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, mkint(XFR_PROTO_MAJOR));
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, mkint(XFR_PROTO_MINOR));
+ if(err) goto exit;
+ err = pack_bool(conn->out, 0);
+ IOStream_flush(conn->out);
+ dprintf("> xfr_response...\n");
+ err = xfr_response(conn);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int xfr_send_xfr(Conn *conn, uint32_t vmid){
+ int err;
+ err = pack_type(conn->out, T_CONS);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, oxfr_xfr);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, mkint(vmid));
+ if(err) goto exit;
+ err = pack_bool(conn->out, 0);
+ if(err) goto exit;
+ exit:
+ return err;
+}
+
+int xfr_send_ok(Conn *conn, uint32_t vmid){
+ int err = 0;
+ err = pack_type(conn->out, T_CONS);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, oxfr_ok);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, mkint(vmid));
+ if(err) goto exit;
+ err = pack_bool(conn->out, 0);
+ exit:
+ return err;
+}
+
+int xfr_send_suspend(Conn *conn, uint32_t vmid){
+ int err = 0;
+
+ err = pack_type(conn->out, T_CONS);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, oxfr_suspend);
+ if(err) goto exit;
+ err = pack_bool(conn->out, 1);
+ if(err) goto exit;
+ err = pack_sxpr(conn->out, mkint(vmid));
+ if(err) goto exit;
+ err = pack_bool(conn->out, 0);
+ exit:
+ return err;
+}
+
+/** Get vm state. Send transfer message.
+ *
+ * @param peer connection
+ * @param msg message
+ * @return 0 on success, error code otherwise
+ */
+int xfr_send_state(XfrState *state, Conn *xend, Conn *peer){
+ int err = 0;
+ Sxpr sxpr;
+
+ dprintf(">\n");
+ XfrState_set_state(state, XFR_STATE);
+ // Send xfr message and the domain state.
+ err = xfr_send_xfr(peer, state->vmid);
+ if(err) goto exit;
+ err = xen_domain_snd(xend, peer->out, state->vmid, state->vmconfig, state->vmconfig_n);
+ if(err) goto exit;
+ IOStream_flush(peer->out);
+ // Read the response from the peer.
+ err = unpack_sxpr(peer->in, &sxpr);
+ if(err) goto exit;
+ if(sxpr_elementp(sxpr, oxfr_err)){
+ // Error.
+ int errcode;
+ err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode);
+ if(!err) err = errcode;
+ } else if(sxpr_elementp(sxpr, oxfr_ok)){
+ // Ok - get the new domain id.
+ err = intof(sxpr_childN(sxpr, 0, ONONE), &state->vmid_new);
+ xfr_error(peer, err);
+ } else {
+ // Anything else is invalid. But it may be too late.
+ err = -EINVAL;
+ xfr_error(peer, err);
+ }
+ exit:
+ XfrState_set_err(state, err);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Finish the transfer.
+ */
+int xfr_send_done(XfrState *state, Conn *xend){
+ int err = 0;
+ int first_err = 0;
+
+ first_err = XfrState_first_err(state);
+ if(first_err){
+ XfrState_set_state(state, XFR_FAIL);
+ } else {
+ XfrState_set_state(state, XFR_DONE);
+ }
+ if(first_err){
+ err = xfr_error(xend, first_err);
+ } else {
+ // Report new domain id to xend.
+ err = xfr_send_ok(xend, state->vmid_new);
+ }
+
+ XfrState_set_err(state, err);
+ if(XfrState_first_err(state)){
+ int s, serr;
+
+ wprintf("> Transfer errors:\n");
+ for(s = 0; s < XFR_MAX; s++){
+ serr = state->state_err[s];
+ if(!serr) continue;
+ wprintf("> state=%-12s err=%d\n", xfr_state_name(s), serr);
+ }
+ } else {
+ wprintf("> Transfer OK\n");
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Migrate a vm to another node.
+ *
+ * @param xend connection
+ * @return 0 on success, error code otherwise
+ */
+int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t port){
+ int err = 0;
+ Conn _peer = {}, *peer = &_peer;
+ int flags = 0;
+ struct in_addr xfr_addr;
+ uint16_t xfr_port;
+ time_t t0 = time(NULL), t1;
+
+ dprintf(">\n");
+ flags |= CONN_NOBUFFER;
+ if(args->compress){
+ flags |= CONN_WRITE_COMPRESS;
+ }
+ xfr_addr.s_addr = addr;
+ xfr_port = port;
+ if(!xfr_port) xfr_port = htons(XFRD_PORT);
+ dprintf("> Xfr vmid=%u\n", state->vmid);
+ dprintf("> Xfr xfr_addr=%s:%d\n", inet_ntoa(xfr_addr), ntohs(xfr_port));
+ err = Conn_connect(peer, flags, xfr_addr, xfr_port);
+ if(err) goto exit;
+ printf("\n");
+ XfrState_set_state(state, XFR_HELLO);
+ // Send hello message.
+ err = xfr_send_hello(peer);
+ if(err) goto exit;
+ printf("\n");
+ // Send vm state.
+ err = xfr_send_state(state, xend, peer);
+ if(err) goto exit;
+ if(args->compress){
+ IOStream *zio = peer->out;
+ int plain_bytes = lzi_stream_plain_bytes(zio);
+ int comp_bytes = lzi_stream_comp_bytes(zio);
+ float ratio = lzi_stream_ratio(zio);
+ dprintf("> Compression: plain %d bytes, compressed %d bytes, ratio %3.2f\n",
+ plain_bytes, comp_bytes, ratio);
+ }
+ printf("\n");
+ exit:
+ dprintf("> err=%d\n", err);
+ if(err && !XfrState_get_err(state)){
+ XfrState_set_err(state, err);
+ }
+ Conn_close(peer);
+ if(!err){
+ t1 = time(NULL) - t0;
+ dprintf("> Transfer complete in %lu seconds\n", t1);
+ }
+ dprintf("> done err=%d, notifying xend...\n", err);
+ xfr_send_done(state, xend);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Save a vm to file.
+ */
+int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){
+ int err = 0;
+ IOStream *io = NULL;
+
+ io = file_stream_fopen(file, "wb");
+ if(!io){
+ err = -EIO;
+ goto exit;
+ }
+ err = xen_domain_snd(xend, io, state->vmid, state->vmconfig, state->vmconfig_n);
+ exit:
+ if(io){
+ IOStream_close(io);
+ IOStream_free(io);
+ }
+ return err;
+}
+
+/** Accept the transfer of a vm from another node.
+ *
+ * @param peer connection
+ * @param msg message
+ * @return 0 on success, error code otherwise
+ */
+int xfr_recv(Args *args, XfrState *state, Conn *peer){
+ int err = 0;
+ time_t t0 = time(NULL), t1;
+
+ dprintf(">\n");
+ err = xen_domain_rcv(peer->in, &state->vmid_new, &state->vmconfig, &state->vmconfig_n);
+ if(err) goto exit;
+
+ err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n);
+ if(err) goto exit;
+
+ // Report new domain id to peer.
+ err = xfr_send_ok(peer, state->vmid_new);
+ if(err) goto exit;
+ // Get the final ok.
+ err = xfr_response(peer);
+ exit:
+ if(!err){
+ t1 = time(NULL) - t0;
+ dprintf("> Transfer complete in %lu seconds\n", t1);
+ }
+ if(err){
+ xfr_error(peer, err);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Listen for a hello followed by a service request.
+ * The request can be from the local xend or from xfrd on another node.
+ *
+ * @param peersock socket
+ * @param peer_in peer address
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){
+ int err = 0;
+ Sxpr sxpr;
+ Conn _conn = {}, *conn = &_conn;
+ int flags = CONN_NOBUFFER;
+
+ dprintf(">\n");
+ err = Conn_init(conn, flags, peersock, peer_in);
+ if(err) goto exit;
+ err = xfr_hello(conn);
+ if(err) goto exit;
+ err = unpack_sxpr(conn->in, &sxpr);
+ if(err) goto exit;
+ if(sxpr_elementp(sxpr, oxfr_migrate)){
+ // Migrate message from xend.
+ uint32_t addr;
+ uint16_t port;
+ XfrState _state = {}, *state = &_state;
+ int n = 0;
+
+ dprintf("> xfr.migrate\n");
+ err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid);
+ if(err) goto exit;
+ err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig);
+ if(err) goto exit;
+ state->vmconfig_n = strlen(state->vmconfig);
+ err = addrof(sxpr_childN(sxpr, n++, ONONE), &addr);
+ if(err) goto exit;
+ err = portof(sxpr_childN(sxpr, n++, ONONE), &port);
+ if(err) goto exit;
+ err = xfr_send(args, state, conn, addr, port);
+
+ } else if(sxpr_elementp(sxpr, oxfr_save)){
+ // Save message from xend.
+ char *file;
+ XfrState _state = {}, *state = &_state;
+ int n = 0;
+
+ dprintf("> xfr.save\n");
+ err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid);
+ if(err) goto exit;
+ err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig);
+ if(err) goto exit;
+ state->vmconfig_n = strlen(state->vmconfig);
+ err = stringof(sxpr_childN(sxpr, n++, ONONE), &file);
+ if(err) goto exit;
+ err = xfr_save(args, state, conn, file);
+
+ } else if(sxpr_elementp(sxpr, oxfr_xfr)){
+ // Xfr message from peer xfrd.
+ XfrState _state = {}, *state = &_state;
+ int n = 0;
+
+ dprintf("> xfr.xfr\n");
+ err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid);
+ if(err) goto exit;
+ err = xfr_recv(args, state, conn);
+
+ } else{
+ // Anything else is invalid.
+ err = -EINVAL;
+ dprintf("> Invalid message: ");
+ objprint(iostderr, sxpr, 0);
+ IOStream_print(iostderr, "\n");
+ xfr_error(conn, err);
+ }
+ exit:
+ Conn_close(conn);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Accept an incoming connection.
+ *
+ * @param sock tcp socket
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_accept(Args *args, int sock){
+ struct sockaddr_in peer_in;
+ struct sockaddr *peer = (struct sockaddr *)&peer_in;
+ socklen_t peer_n = sizeof(peer_in);
+ int peersock;
+ pid_t pid;
+ int err = 0;
+
+ dprintf(">\n");
+ dprintf("> accept...\n");
+ peersock = accept(sock, peer, &peer_n);
+ dprintf("> accept=%d\n", peersock);
+ if(peersock < 0){
+ perror("accept");
+ err = -errno;
+ goto exit;
+ }
+ iprintf("> Accepted connection from %s:%d\n",
+ inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+ pid = fork();
+ if(pid > 0){
+ // Parent, fork succeeded.
+ iprintf("> Forked child pid=%d\n", pid);
+ close(peersock);
+ } else if (pid < 0){
+ // Parent, fork failed.
+ perror("fork");
+ close(peersock);
+ } else {
+ // Child.
+ iprintf("> Xfr service for %s:%d\n",
+ inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+ err = xfrd_service(args, peersock, peer_in);
+ iprintf("> Xfr service err=%d\n", err);
+ shutdown(peersock, 2);
+ exit(err ? 1 : 0);
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Socket select loop.
+ * Accepts connections on the tcp socket.
+ *
+ * @param listen_sock tcp listen socket
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_select(Args *args, int listen_sock){
+ int err = 0;
+ SelectSet set = {};
+ dprintf("> socks: %d\n", listen_sock);
+ while(1){
+ SelectSet_zero(&set);
+ SelectSet_add_read(&set, listen_sock);
+ err = SelectSet_select(&set, NULL);
+ if(err < 0){
+ if(errno == EINTR) continue;
+ perror("select");
+ goto exit;
+ }
+ if(FD_ISSET(listen_sock, &set.rd)){
+ xfrd_accept(args, listen_sock);
+ }
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create a socket.
+ *
+ * @param args program arguments
+ * @param socktype socket type
+ * @param reuse whether to set SO_REUSEADDR
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int create_socket(Args *args, int socktype, int reuse, int *val){
+ int err = 0;
+ int sock = 0;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ socklen_t addr_n = sizeof(addr_in);
+
+ dprintf(">\n");
+ // Create socket and bind it.
+ sock = socket(AF_INET, socktype, 0);
+ if(sock < 0){
+ err = -errno;
+ goto exit;
+ }
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr.s_addr = INADDR_ANY;
+ addr_in.sin_port = args->port;
+ dprintf("> port=%d\n", ntohs(addr_in.sin_port));
+ if(reuse){
+ // Set socket option to reuse address.
+ int val = 1;
+ err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ if(err < 0){
+ err = -errno;
+ perror("setsockopt");
+ goto exit;
+ }
+ }
+ err = bind(sock, addr, addr_n);
+ if(err < 0){
+ err = -errno;
+ perror("bind");
+ goto exit;
+ }
+ exit:
+ *val = (err ? -1 : sock);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create the tcp listen socket.
+ *
+ * @param args program arguments
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_listen_socket(Args *args, int *val){
+ int err = 0;
+ int sock;
+ dprintf(">\n");
+ err = create_socket(args, SOCK_STREAM, 1, &sock);
+ if(err) goto exit;
+ dprintf("> listen...\n");
+ err = listen(sock, 5);
+ if(err < 0){
+ err = -errno;
+ perror("listen");
+ goto exit;
+ }
+ exit:
+ *val = (err ? -1 : sock);
+ if(err) close(sock);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Type for signal handling functions. */
+typedef void SignalAction(int code, siginfo_t *info, void *data);
+
+/** Handle SIGCHLD by getting child exit status.
+ * This prevents child processes being defunct.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+void sigaction_SIGCHLD(int code, siginfo_t *info, void *data){
+ int status;
+ pid_t pid;
+ //dprintf("> child_exit=%d waiting...\n", child_exit);
+ pid = wait(&status);
+ dprintf("> child pid=%d status=%d\n", pid, status);
+}
+
+/** Handle SIGPIPE.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+void sigaction_SIGPIPE(int code, siginfo_t *info, void *data){
+ dprintf("> SIGPIPE\n");
+ //fflush(stdout);
+ //fflush(stderr);
+ //exit(1);
+}
+
+/** Handle SIGALRM.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+void sigaction_SIGALRM(int code, siginfo_t *info, void *data){
+ dprintf("> SIGALRM\n");
+}
+
+/** Install a handler for a signal.
+ *
+ * @param signum signal
+ * @param action handler
+ * @return 0 on success, error code otherwise
+ */
+int catch_signal(int signum, SignalAction *action){
+ int err = 0;
+ struct sigaction sig = {};
+ sig.sa_sigaction = action;
+ sig.sa_flags = SA_SIGINFO;
+ err = sigaction(signum, &sig, NULL);
+ if(err){
+ perror("sigaction");
+ }
+ return err;
+}
+
+/** Transfer daemon main program.
+ *
+ * @param args program arguments
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_main(Args *args){
+ int err = 0;
+ int listen_sock;
+
+ dprintf(">\n");
+ catch_signal(SIGCHLD,sigaction_SIGCHLD);
+ catch_signal(SIGPIPE,sigaction_SIGPIPE);
+ catch_signal(SIGALRM,sigaction_SIGALRM);
+ err = xfrd_listen_socket(args, &listen_sock);
+ if(err) goto exit;
+ err = xfrd_select(args, listen_sock);
+ exit:
+ close(listen_sock);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Parse command-line arguments and call the xfrd main program.
+ *
+ * @param arg argument count
+ * @param argv arguments
+ * @return 0 on success, 1 otherwise
+ */
+int main(int argc, char *argv[]){
+ int err = 0;
+ int key = 0;
+ int long_index = 0;
+
+ set_defaults(args);
+ while(1){
+ key = getopt_long(argc, argv, short_opts, long_opts, &long_index);
+ if(key == -1) break;
+ switch(key){
+ case OPT_PORT:
+ err = !convert_service_to_port(optarg, &args->port);
+ if(err) goto exit;
+ break;
+ case OPT_COMPRESS:
+ args->compress = TRUE;
+ break;
+ case OPT_HELP:
+ usage(0);
+ break;
+ case OPT_VERBOSE:
+ args->verbose = TRUE;
+ break;
+ case OPT_VERSION:
+ printf("> Version %d.%d\n", XFR_PROTO_MAJOR, XFR_PROTO_MINOR);
+ exit(0);
+ break;
+ default:
+ usage(EINVAL);
+ break;
+ }
+ }
+ xfr_init();
+ err = xfrd_main(args);
+ exit:
+ if(err && key > 0){
+ fprintf(stderr, "Error in arg %c\n", key);
+ }
+ return (err ? 1 : 0);
+}
+
--- /dev/null
+#ifndef _XFRD_XFRD_H_
+#define _XFRD_XFRD_H_
+
+/** Xend port in host order. */
+#define XEND_PORT 8001
+
+/** Xfrd port in host order. */
+#define XFRD_PORT 8002
+
+/** Protocol version. */
+#define XFR_PROTO_MAJOR 1
+#define XFR_PROTO_MINOR 0
+
+#endif
--- /dev/null
+#!/bin/env python
+"""
+Test client for the migration daemon (xfrd).
+
+Author: Mike Wray <mike.wray@hp.com>
+
+"""
+import getopt
+import sys
+import os
+from socket import *
+import StringIO
+
+sys.path.append("/home/mjw/repos-bk/xeno-unstable.bk/tools/python")
+
+import xen.xend.sxp as sxp
+from xen.xend.packing import SxpPacker, SxpUnpacker
+
+XFRD_PORT = 8002
+
+verbose = 0
+
+class TCPClient:
+
+ def __init__(self, host, port):
+ print ">TCPClient"
+ self.sock = socket(AF_INET, SOCK_STREAM, 0)
+ print ">TCPClient sock=", self.sock
+ print ">TCPClient> connect ", host, port
+ v = self.sock.connect((host, port))
+ print ">TCPClient> connect=", v
+ # Send plain header (no gzip).
+ #self.sock.send("\0\0")
+
+ self.sockin = self.sock.makefile("r")
+ self.sockout = self.sock.makefile("w")
+ self.packer = SxpPacker(self.sockout)
+ self.unpacker = SxpUnpacker(self.sockin)
+ #pass
+
+ def request(self, req):
+ print "request>", req
+ self.packer.pack(req)
+ self.sockout.flush()
+ print "request<"
+
+ def request_hello(self):
+ self.request(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR])
+
+ def request_migrate(self, vmid, vhost, vport, vmconfig='(vm)'):
+ self.request(['xfr.migrate', vmid, vmconfig, vhost, vport])
+
+ def read(self):
+ while(1):
+ v = self.unpacker.unpack()
+ print 'read>', v
+ if v[0] == 'xfr.err' and v[1]: return
+ if v[0] == 'xfr.ok': return
+
+XFR_PROTO_MAJOR = 1
+XFR_PROTO_MINOR = 0
+
+host_default = "localhost"
+port_default = XFRD_PORT
+vhost_default = "localhost"
+vport_default = 8003
+vmid_default = 1
+
+# Short options. Options followed by ':' need a parameter.
+short_opts = 'h'
+
+# Long options. Options ending in '=' need a parameter.
+long_opts = [ 'host=', 'port=', 'vhost=', 'vport=', 'vmid=', 'verbose', 'help']
+
+def usage(err=None):
+ if err:
+ out = sys.stderr
+ else:
+ out = sys.stdout
+ print >> out, 'Usage: %s [options] [command...]\n' % sys.argv[0]
+ print >> out, '--host <host>\n\tHost to initiate transfer on. Default %s.' % host_default
+ print >> out, '--port <port>\n\tPort to initiate transfer on. Default %d.' % port_default
+ print >> out, '--vhost <vhost>\n\tHost to transfer VM to. Default %s.' % vhost_default
+ print >> out, '--vport <vport>\n\tPort to transfer VM to. Default %d.' % vport_default
+ print >> out, '--vmid <vmid>\n\tVM id. Default %d.' % vmid_default
+ print >> out, '--help\n\tPrint help.'
+
+def main(argv):
+ global verbose
+ host = host_default
+ port = port_default
+ vhost = vhost_default
+ vport = vport_default
+ vmid = vmid_default
+
+ try:
+ opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+ except getopt.GetoptError, ex:
+ print >>sys.stderr, 'Error:', ex
+ usage(1)
+ sys.exit(1)
+
+ for key, val in opts:
+ if key == '--help':
+ usage()
+ sys.exit(0)
+ elif key == '--host':
+ host = val
+ elif key == '--port':
+ port = int(val)
+ elif key == '--vhost':
+ vhost = val
+ elif key == '--vport':
+ vport = int(val)
+ elif key == '--vmid':
+ vmid = int(val)
+
+ print "host=%s port=%d" % (host, port)
+ print "vhost=%s vport=%d vmid=%d" % (vhost, vport, vmid)
+ client = TCPClient(gethostbyname(host), port)
+ client.request_hello()
+ client.request_migrate(vmid, gethostbyname(vhost), vport)
+ client.read()
+
+if __name__ == '__main__':
+ main(sys.argv)
+